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