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]; if (instruction.Operand is MethodReference methodReference) { if (string.Equals(methodReference.Name, "GetCurrentClassLogger")) { // We have a possible match var getLoggerMethod = GetGetLoggerMethod(methodReference.DeclaringType); if (getLoggerMethod is 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.WriteWarningPoint(message, point); } else { FodyEnvironment.WriteWarning(message); } continue; } FodyEnvironment.WriteDebug($"Weaving auto log to specific log for '{type.FullName}'"); 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))); } } } }
public void Execute() { foreach (var type in _allTypes) { try { var weaver = new ArgumentWeaver(type, _msCoreReferenceFinder, _configuration); weaver.Execute(); } catch (Exception ex) { FodyEnvironment.WriteWarning($"Failed to weave type '{type.FullName}', message is '{ex.Message}'"); return; } } }
public void Execute() { foreach (var type in _allTypes) { try { var weaver = new LoggingWeaver(type); weaver.Execute(); } catch (Exception ex) { FodyEnvironment.WriteWarning($"Failed to weave type '{type.FullName}', message is '{ex.Message}'"); return; } } }
public void Execute() { var loggingFields = (from field in _type.Fields where field.IsStatic && string.Equals(field.FieldType.FullName, CatelLoggingClass) select field).ToList(); if (loggingFields.Count == 0) { return; } var staticConstructor = _type.GetStaticConstructor(); if (staticConstructor is null) { FodyEnvironment.WriteWarning($"Cannot weave ILog fields without a static constructor, ignoring type '{_type.FullName}'"); return; } FodyEnvironment.WriteDebug($"\tExecuting '{GetType().Name}' for '{_type.FullName}'"); var body = staticConstructor.Body; body.SimplifyMacros(); try { UpdateCallsToGetCurrentClassLogger(body); } catch (Exception ex) { FodyEnvironment.WriteWarning($"Failed to update static log definition in '{_type.FullName}', '{ex.Message}'"); } body.OptimizeMacros(); staticConstructor.UpdateDebugInfo(); }
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; } var isAsyncMethod = method.IsAsyncMethod(); if (isAsyncMethod) { // Too complex for now, see https://github.com/Catel/Catel.Fody/issues/33 FodyEnvironment.WriteWarning($"Method '{method.GetFullName()}' should no longer use display class '{displayClassType.GetFullName()}', but optimization for async methods has been turned off for now (see https://github.com/Catel/Catel.Fody/issues/33 for details)"); return; } FodyEnvironment.WriteDebug($"Method '{method.GetFullName()}' no longer uses display class '{displayClassType.GetFullName()}', removing the display class from the method"); // Remove special constructors if (method.IsConstructor) { // We need to delete from the newobj => call to base constructor: // L_000c: ldarg.0 // L_000d: call instance void [mscorlib]System.Object::.ctor() for (var i = 0; i < instructions.Count; i++) { var innerInstruction = instructions[i]; if (innerInstruction.OpCode == OpCodes.Newobj) { var remove = innerInstruction.UsesObjectFromDeclaringTypeName(displayClassType.Name); if (remove) { var startIndex = i; var endIndex = i; for (var j = i + 1; j < instructions.Count; j++) { var nextInstruction = instructions[j]; if (nextInstruction.IsOpCode(OpCodes.Ldarg, OpCodes.Ldarg_0)) { var nextNextInstruction = instructions[j + 1]; if (nextNextInstruction.IsCallToMethodName(".ctor") || nextNextInstruction.IsCallToMethodName(".cctor")) { break; } } endIndex = j; } instructions.RemoveInstructionsFromPositions(startIndex, endIndex); } } } } // 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 // // Async methods (note that in release mode, the compiler won't generate a field for the display class, and we need to // support both debug and release mode): // // DEBUG MODE (display class stored in a field) // L_0000: ldarg.0 // L_0001: ldfld int32 Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass /< CheckForNullAsync > d__16::<> 1__state // L_0006: stloc.0 // L_0007: ldarg.0 // L_0008: newobj instance void Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass /<> c__DisplayClass16_0::.ctor() // L_000d: stfld class Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass/<>c__DisplayClass16_0 Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass/<CheckForNullAsync>d__16::<>8__1 // L_0012: ldarg.0 // L_0013: ldfld class Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass/<>c__DisplayClass16_0 Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass/<CheckForNullAsync>d__16::<>8__1 // L_0018: ldarg.0 // L_0019: ldfld object Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass/<CheckForNullAsync>d__16::myObject // L_001e: stfld object Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass/<>c__DisplayClass16_0::myObject // L_0023: nop // // RELEASE MODE (display class not stored in a field) // L_0000: newobj instance void Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass /<> c__DisplayClass16_0::.ctor() // L_0005: dup // L_0006: ldarg.0 // L_0007: ldfld object Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass /< CheckForNullAsync > d__16::myObject // L_000c: stfld object Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass /<> c__DisplayClass16_0::myObject // L_0011: ldtoken Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass /<> c__DisplayClass16_0 // L_0016: call class [mscorlib] System.Type[mscorlib] System.Type::GetTypeFromHandle(valuetype[mscorlib] System.RuntimeTypeHandle) // L_001b: call class [System.Core] System.Linq.Expressions.ConstantExpression[System.Core] System.Linq.Expressions.Expression::Constant(object, class [mscorlib] System.Type) // L_0020: ldtoken object Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass/<>c__DisplayClass16_0::myObject // L_0025: call class [mscorlib] System.Reflection.FieldInfo[mscorlib] System.Reflection.FieldInfo::GetFieldFromHandle(valuetype[mscorlib] System.RuntimeFieldHandle) // L_002a: call class [System.Core] System.Linq.Expressions.MemberExpression[System.Core] System.Linq.Expressions.Expression::Field(class [System.Core] System.Linq.Expressions.Expression, class [mscorlib] System.Reflection.FieldInfo) // L_002f: call !!0[] [mscorlib] System.Array::Empty<class [System.Core] System.Linq.Expressions.ParameterExpression>() // L_0034: call class [System.Core] System.Linq.Expressions.Expression`1<!!0> [System.Core] System.Linq.Expressions.Expression::Lambda<class [mscorlib] System.Func`1<object>>(class [System.Core] System.Linq.Expressions.Expression, class [System.Core] System.Linq.Expressions.ParameterExpression[]) // L_0039: call void[Catel.Core] Catel.Argument::IsNotNull<object>(class [System.Core] System.Linq.Expressions.Expression`1<class [mscorlib] System.Func`1<!!0>>) // L_003e: leave.s L_0057 for (var i = 0; i < instructions.Count; i++) { var innerInstruction = instructions[i]; if (innerInstruction.OpCode == OpCodes.Newobj) { var remove = innerInstruction.UsesObjectFromDeclaringTypeName(displayClassType.Name); if (remove) { var startIndex = i; var endIndex = i; // If the next instruction is stloc, remove that one as well if (!isAsyncMethod) { var nextIndex = i + 1; if (nextIndex < instructions.Count) { if (instructions[nextIndex].IsOpCode(OpCodes.Stloc, OpCodes.Stloc_0)) { endIndex = nextIndex; } } } else { // In async methods, we need to delete more since the values are stored in a field var previousIndex = i - 1; if (previousIndex >= 0) { if (instructions[previousIndex].IsOpCode(OpCodes.Ldarg, OpCodes.Ldarg_0)) { startIndex--; } } // Search var fieldOfDisplayClass = method.DeclaringType.Fields.FirstOrDefault(x => x.FieldType.Name.Equals(displayClassType.Name)); if (fieldOfDisplayClass != null) { // Async in DEBUG mode for (var j = i + 1; j < instructions.Count - 1; j++) { var currentInstruction = instructions[j]; var nextInstruction = instructions[j + 1]; if (currentInstruction.UsesField(fieldOfDisplayClass)) { endIndex = j; if (nextInstruction.IsOpCode(OpCodes.Ldarg, OpCodes.Ldarg_0)) { endIndex = j + 1; // Skip next instruction check, just handled it j++; } } else { break; } } } else { // Async in RELEASE mode for (var j = i + 1; j < instructions.Count - 1; j++) { var currentInstruction = instructions[j]; if (currentInstruction.IsOpCode(OpCodes.Dup)) { endIndex = j; continue; } var nextInstruction = instructions[j + 1]; if (currentInstruction.UsesObjectFromDeclaringTypeName(displayClassType.Name)) { endIndex = j; if (nextInstruction.IsOpCode(OpCodes.Ldarg, OpCodes.Ldarg_0)) { endIndex = j + 1; // Skip next instruction check, just handled it j++; } } else { break; } } } } instructions.RemoveInstructionsFromPositions(startIndex, endIndex); } } } // Remove assignments in async methods // Option A: // L_0012: ldarg.0 // L_0013: ldfld class Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass/<>c__DisplayClass18_0 Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass/<CheckForNullAsync_MultipleParameters>d__18::<>8__1 // // Option B: // L_0018: ldarg.0 // L_0019: ldfld object Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass/<CheckForNullAsync_MultipleParameters>d__18::myObject1 // L_001e: stfld object Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass/<>c__DisplayClass18_0::myObject1 if (isAsyncMethod) { var fieldOfDisplayClass = method.DeclaringType.Fields.FirstOrDefault(x => x.FieldType.Name.Equals(displayClassType.Name)); for (var i = 1; i < instructions.Count - 2; i++) { var instruction = instructions[i]; if (instruction.IsOpCode(OpCodes.Ldfld)) { var startIndex = i; var endIndex = i; if (fieldOfDisplayClass != null && instruction.UsesField(fieldOfDisplayClass)) { // Option A endIndex = i; var previousInstruction = instructions[i - 1]; if (previousInstruction.IsOpCode(OpCodes.Ldarg)) { startIndex = i - 1; } } var nextInstruction = instructions[i + 1]; if (nextInstruction.UsesType(displayClassType, OpCodes.Stfld)) { // Option B endIndex = i + 1; var previousInstruction = instructions[i - 1]; if (previousInstruction.IsOpCode(OpCodes.Ldarg)) { startIndex = i - 1; } } if (endIndex > startIndex) { instructions.RemoveInstructionsFromPositions(startIndex, endIndex); // Always reset i = 1; } } } } // Remove display class allocation and assignments // 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); if (i > 1) { instructions.RemoveAt(i - 1); } // Special case, we only need to remove when i - 2 is ldlock.s var additionalIndex = i - 2; if (additionalIndex >= 0) { var instruction = instructions[additionalIndex]; if (instruction.IsOpCode(OpCodes.Ldloc_S, OpCodes.Ldloc)) { if (instruction.Operand is VariableReference operand) { var variableType = operand.VariableType; if (variableType.IsGenericInstance) { variableType = variableType.GetElementType(); } if (variableType == displayClassType) { instructions.RemoveAt(additionalIndex); } } } } // Reset index & start over i -= 3; if (i < 0) { i = 0; } } } // Remove display class loading for (var i = 0; i < instructions.Count; i++) { var innerInstruction = instructions[i]; if (innerInstruction.UsesType(displayClassType, OpCodes.Ldtoken)) { instructions.RemoveAt(i--); } } // Remove display class - variables (regular methods) 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); i--; } } // Remove display class - fields (async methods) for (var i = 0; i < method.DeclaringType.Fields.Count; i++) { var field = method.DeclaringType.Fields[i]; if (string.Equals(field.FieldType.Name, displayClassType.Name)) { method.DeclaringType.Fields.RemoveAt(i); i--; } } // Remove unused fields (clean up async methods that we have optimized, we don't check non-async because it // would require to check *all* methods of a class) if (isAsyncMethod) { for (var i = 0; i < method.DeclaringType.Fields.Count; i++) { var methodDeclaringType = method.DeclaringType; var field = method.DeclaringType.Fields[i]; if (!field.IsPrivate) { continue; } // Separate check for current method since this method is already simplified (and we don't want to optimize it yet) if (instructions.UsesField(field)) { continue; } var anyMethodUsesField = methodDeclaringType.Methods.Any(x => { var usesField = false; if (!x.Name.Equals(method.Name)) { var body = x.Body; body.SimplifyMacros(); usesField = body.Instructions.UsesField(field); body.OptimizeMacros(); } return(usesField); }); if (!anyMethodUsesField) { method.DeclaringType.Fields.RemoveAt(i); i--; } } } // Remove display class from container var declaringType = displayClassType.DeclaringType; declaringType?.NestedTypes.Remove(displayClassType); // Special case, remove any Dup opcodes before the argument checks for (var i = 0; i < instructions.Count; i++) { var remove = false; var innerInstruction = instructions[i]; if (innerInstruction.IsOpCode(OpCodes.Dup)) { // If we have a non-expression argument call within 4 instructions, remove this one for (var j = i + 1; j <= i + 5; j++) { if (j < instructions.Count) { var nextInstruction = instructions[j]; if (nextInstruction.IsOpCode(OpCodes.Call)) { if (nextInstruction.Operand is MethodReference operand) { if (operand.DeclaringType.Name.Contains("Argument") && operand.Parameters[0].ParameterType.Name.Contains("String")) { remove = true; } } } } } } if (remove) { instructions.RemoveAt(i--); } } // 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 bool AddOrUpdateOnPropertyChangedMethod(PropertyDefinition property) { if (property.HasParameters) { // Not supported return(true); } var getMethodReference = _catelType.TypeDefinition.Module.ImportReference(_catelType.GetPropertyChangedEventArgsTypeForCurrentCatelVersion().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 is null) { FodyEnvironment.WriteWarning($"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) is 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 List <PrivateReference> FindPrivateReferences() { var csProj = _moduleWeaver.ProjectFilePath; if (string.IsNullOrWhiteSpace(csProj) || !File.Exists(csProj)) { return(new List <PrivateReference>()); } // Assembly name != package name, so we need to go through all *private* packages to // see if it's a private reference. For now we have a *simple* reference structure, // which means only modern sdk project style is supported, and only references directly // listed in the csproj will be supported var privateReferencesCache = CacheHelper.GetCache <Dictionary <string, List <PrivateReference> > >(csProj); if (!privateReferencesCache.TryGetValue(csProj, out var privateReferences)) { privateReferences = new List <PrivateReference>(); try { var element = XElement.Parse(File.ReadAllText(csProj)); var packageReferenceElements = element.XPathSelectElements("//PackageReference"); foreach (var packageReferenceElement in packageReferenceElements) { var includeAttribute = packageReferenceElement.Attribute("Include"); if (includeAttribute is null) { continue; } var packageName = includeAttribute.Value; var versionAttribute = packageReferenceElement.Attribute("Version"); if (versionAttribute is null) { FodyEnvironment.WriteWarning($"Could not find version attribute for '{packageName}'"); continue; } var version = versionAttribute.Value; var privateAssetsAttribute = packageReferenceElement.Attribute("PrivateAssets"); if (privateAssetsAttribute != null) { if (string.Equals(privateAssetsAttribute.Value, "all", StringComparison.OrdinalIgnoreCase)) { privateReferences.Add(new PrivateReference(packageName, version)); continue; } } var privateAssetsElement = packageReferenceElement.Element("PrivateAssets"); if (privateAssetsElement != null) { if (string.Equals(privateAssetsElement.Value, "all", StringComparison.OrdinalIgnoreCase)) { privateReferences.Add(new PrivateReference(packageName, version)); continue; } } } } catch (Exception ex) { FodyEnvironment.WriteError($"Failed to search for private packages in project file '{csProj}':\n{ex}"); } privateReferencesCache[csProj] = privateReferences; } return(privateReferences); }
private void ProcessMethod(MethodDefinition method) { if (method.Body is null) { return; } if (method.IsDecoratedWithAttribute("NoWeavingAttribute")) { FodyEnvironment.WriteDebug($"\t\tSkipping '{method.Name}' because 'Catel.Fody.NoWeavingAttribute'"); 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.WriteDebug($"\tExecuting '{GetType().Name}' for '{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 is 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.WriteErrorPoint($"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 beginning of the ctor) if (instructions != null || ContainsArgumentChecks(method)) { if (instructions is 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)) { if (_configuration.IsRunningAgainstCatel) { FodyEnvironment.WriteError($"Weaving argument checks is disabled for Catel itself, ensure writing performant code by calling the non-expression version in '{method.GetFullName()}'"); continue; } var fullKey = ((MethodReference)instruction.Operand).GetFullName(); var parameterOrField = GetParameterOrFieldForExpressionArgumentCheck(method, instructions, instruction); if (parameterOrField is null) { FodyEnvironment.WriteWarning($"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 is null) { FodyEnvironment.WriteWarningPoint($"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.GetSequencePoint(instruction)); continue; } var removedInfo = RemoveArgumentWeavingCall(method, instructions, instruction); if (!displayClasses.Contains(removedInfo.DisplayClassTypeDefinition)) { displayClasses.Add(removedInfo.DisplayClassTypeDefinition); } var weaver = ArgumentMethodCallWeaverBase.WellKnownWeavers[customAttribute.AttributeType.FullName]; if (!weaver.Execute(_typeDefinition, method, parameterOrField, customAttribute, removedInfo.Index)) { // 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); } } // Step 4) Remove double nop commands, start at 1 // Note: disabled because there might be jump codes to different Nop instructions //for (int i = 1; i < instructions.Count; i++) //{ // if (instructions[i].IsOpCode(OpCodes.Nop) && instructions[i - 1].IsOpCode(OpCodes.Nop)) // { // instructions.RemoveAt(i--); // } //} } if (instructions != null) { method.Body.OptimizeMacros(); method.UpdateDebugInfo(); } }