Mono.Cecil library
:
public interface IArgumentValidator
{
void Validate(object argument);
}
ValidateMetaFieldsAttribute
:
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class ValidateMetaFieldsAttribute : Attribute, IArgumentValidator
{
public void Validate(object argument)
{
}
}
IL-Rewriter
, Mono.Cecil nuget.
:
var assembly = AssemblyDefinition.ReadAssembly(@"ClassLibrary1.dll");
var module = assembly.MainModule;
IArgumentValidator
-:
var validatorInterface = module.Types
.FirstOrDefault(t => t.IsInterface && t.Name == "IArgumentValidator");
var validators = module.Types
.Where(t => t.Interfaces.Contains(validatorInterface)).ToArray();
, :
var typesToPatch = module.Types.Select(t => new
{
Type = t,
Validators =
t.Methods.SelectMany(
m => m.Parameters.SelectMany(
p => p.CustomAttributes.Select(a => a.AttributeType)))
.Distinct()
.ToArray()
})
.Where(x => x.Validators.Any(v => validators.Contains(v)))
.ToArray();
, ( )
foreach (var typeAndValidators in typesToPatch)
{
var type = typeAndValidators.Type;
var newFields = new Dictionary<TypeReference, FieldDefinition>();
const string namePrefix = "e47bc57b_";
foreach (var validator in typeAndValidators.Validators)
{
var fieldName = $"{namePrefix}{validator.Name}";
var fieldDefinition = new FieldDefinition(fieldName, FieldAttributes.Private, validator);
type.Fields.Add(fieldDefinition);
newFields.Add(validator, fieldDefinition);
}
, . :
var initFields = new MethodDefinition($"{namePrefix}InitFields", MethodAttributes.Private, module.TypeSystem.Void);
foreach (var field in newFields)
{
initFields.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
initFields.Body.Instructions.Add(Instruction.Create(OpCodes.Newobj, field.Key.Resolve().GetConstructors().First()));
initFields.Body.Instructions.Add(Instruction.Create(OpCodes.Stfld, field.Value));
}
initFields.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
type.Methods.Add(initFields);
, . , :
var ctors = type.GetConstructors().ToArray();
var rootCtors = ctors.Where(c =>
!c.Body.Instructions.Any(i => i.OpCode == OpCodes.Call
&& ctors.Except(new []{c}).Any(c2 => c2.Equals(i.Operand)))).ToArray();
foreach (var ctor in rootCtors)
{
var retIdx = ctor.Body.Instructions.Count - 1;
ctor.Body.Instructions.Insert(retIdx, Instruction.Create(OpCodes.Ldarg_0));
ctor.Body.Instructions.Insert(retIdx + 1, Instruction.Create(OpCodes.Call, initFields));
}
( rootCtors
. , , , )
, , - ,
foreach (var method in type.Methods)
{
foreach (var parameter in method.Parameters)
{
foreach (var attribute in parameter.CustomAttributes)
{
if (!validators.Contains(attribute.AttributeType))
continue;
var field = newFields[attribute.AttributeType];
var validationMethod = field.FieldType.Resolve().Methods.First(m => m.Name == "Validate");
method.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Ldarg_0));
method.Body.Instructions.Insert(1, Instruction.Create(OpCodes.Ldfld, field));
method.Body.Instructions.Insert(2, Instruction.Create(OpCodes.Ldarg_S, parameter));
method.Body.Instructions.Insert(3, Instruction.Create(OpCodes.Callvirt, validationMethod));
}
}
}
, ,
assembly.Write("PatchedAssembly.dll");
</" > ( dotPeek)
:
public class Demo
{
public Component SaveComponent([ValidateMetaFields] Component componentToSave)
{
return componentToSave;
}
}
-:
public class Demo
{
private ValidateMetaFieldsAttribute e47bc57b_ValidateMetaFieldsAttribute;
public Component SaveComponent([ValidateMetaFields] Component componentToSave)
{
this.e47bc57b_ValidateMetaFieldsAttribute.Validate((object) componentToSave);
return componentToSave;
}
public Demo()
{
this.e47bc57b_InitFields();
}
private void e47bc57b_InitFields()
{
this.e47bc57b_ValidateMetaFieldsAttribute = new ValidateMetaFieldsAttribute();
}
}