public bool Execute(TypeDefinition type, MethodDefinition methodDefinition, object parameterDefinitionOrFieldDefinition, CustomAttribute attribute, int instructionIndex) { TypeReference targetType = null; MethodDefinition selectedMethod = null; var parameterDefinition = parameterDefinitionOrFieldDefinition as ParameterDefinition; if (parameterDefinition != null) { targetType = parameterDefinition.ParameterType; } var fieldDefinition = parameterDefinitionOrFieldDefinition as FieldDefinition; if (fieldDefinition != null) { targetType = fieldDefinition.FieldType; } if (targetType != null) { try { SelectMethod(_argumentTypeDefinition, targetType, out selectedMethod); } catch (Exception ex) { var error = $"[{type.FullName}.{methodDefinition.Name}] {ex.Message}"; var sequencePoint = methodDefinition.GetFirstSequencePoint(); if (sequencePoint != null) { FodyEnvironment.WriteErrorPoint(error, sequencePoint); } else { FodyEnvironment.WriteError(error); } return(false); } } if (selectedMethod is null) { return(false); } var moduleDefinition = type.Module; var importedMethod = moduleDefinition.ImportReference(selectedMethod); var instructions = new List <Instruction>(); if (parameterDefinition != null) { BuildInstructions(moduleDefinition, type, methodDefinition, parameterDefinition, attribute, instructions); } if (fieldDefinition != null) { BuildInstructions(moduleDefinition, type, methodDefinition, fieldDefinition, attribute, instructions); } if (importedMethod.HasGenericParameters) { var genericInstanceMethod = new GenericInstanceMethod(importedMethod); genericInstanceMethod.GenericArguments.Add(targetType); instructions.Add(Instruction.Create(OpCodes.Call, genericInstanceMethod)); } else { instructions.Add(Instruction.Create(OpCodes.Call, importedMethod)); } methodDefinition.Body.Instructions.Insert(instructionIndex, instructions); return(true); }
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(); } }