Сообщение Re[6]: Удобный способ внесения изменений в чужую C#-библиоте от 26.04.2016 2:42
Изменено 27.04.2016 10:39 Albeoris
Заключительная версия, которая заменяет ссылки на старый тип новыми.
Неполная (отсутствует поддержка исключений, атрибутов). Но в моём сценарии пока работает. Буду допиливать по мере необходимости.
Неполная (отсутствует поддержка исключений, атрибутов). Но в моём сценарии пока работает. Буду допиливать по мере необходимости.
public sealed class TypeReplaser
{
private readonly String _fullName;
private readonly TypeReference _newReference;
private readonly TypeDefinition _newDefination;
private ModuleDefinition _module;
private MethodDefinition _method;
public TypeReplaser(String fullName, TypeReference newReference)
{
_fullName = fullName;
_newReference = newReference;
_newDefination = newReference.Resolve();
}
public void Replase(ModuleDefinition module)
{
_module = module;
foreach (TypeDefinition type in _module.Types)
ProcessType(type);
}
private void ProcessType(TypeDefinition type)
{
if (type.FullName == _fullName)
return; // Пропускаем заменяемый тип, иначе не сможем менять приватные члены
ProcessProperties(type);
ProcessFields(type);
ProcessMethods(type);
ProcessNestedTypes(type);
}
private void ProcessNestedTypes(TypeDefinition type)
{
if (!type.HasNestedTypes)
return;
foreach (TypeDefinition nested in type.NestedTypes)
ProcessType(nested);
}
private void ProcessProperties(TypeDefinition type)
{
foreach (PropertyDefinition p in type.Properties)
{
if (p.PropertyType.FullName == _fullName)
p.PropertyType = _newReference;
}
}
private void ProcessFields(TypeDefinition type)
{
foreach (FieldDefinition f in type.Fields)
{
if (f.FieldType.FullName == _fullName)
f.FieldType = _newReference;
}
}
private void ProcessMethods(TypeDefinition type)
{
foreach (MethodDefinition m in type.Methods)
{
_method = m;
ProcessMethod();
}
}
private void ProcessMethod()
{
if (_method.ReturnType.FullName == _fullName)
_method.ReturnType = _newReference;
ProcessMethodParameters();
ProcessMethodBody();
}
private void ProcessMethodParameters()
{
if (!_method.HasParameters)
return;
foreach (ParameterDefinition p in _method.Parameters)
{
if (p.ParameterType.FullName == _fullName)
p.ParameterType = _newReference;
}
}
private void ProcessMethodBody()
{
if (!_method.HasBody)
return;
ProcessMethodBodyVariables();
ProcessMethodBodyInstructions();
}
private void ProcessMethodBodyVariables()
{
if (!_method.Body.HasVariables)
return;
foreach (VariableDefinition v in _method.Body.Variables)
{
if (v.VariableType.FullName == _fullName)
v.VariableType = _newReference;
}
}
private void ProcessMethodBodyInstructions()
{
MethodBody body = _method.Body;
Collection<Instruction> instructions = body.Instructions;
for (int i = 0; i < instructions.Count; i++)
{
Instruction inst = instructions[i];
switch (inst.OpCode.OperandType)
{
case OperandType.InlineField:
case OperandType.InlineMethod:
case OperandType.InlineTok:
case OperandType.InlineType:
break;
default:
continue;
}
IGenericInstance genericInstance = inst.Operand as IGenericInstance;
if (genericInstance != null && genericInstance.HasGenericArguments)
{
for (int g = 0; g < genericInstance.GenericArguments.Count; g++)
{
if (genericInstance.GenericArguments[g].FullName == _fullName)
genericInstance.GenericArguments[g] = _newReference;
}
}
TypeReference typeReference = inst.Operand as TypeReference;
if (typeReference != null)
{
if (typeReference.FullName == _fullName)
instructions.Replace(i, RecreateTypeOperand(inst));
continue;
}
MethodReference methodReference = inst.Operand as MethodReference;
if (methodReference != null)
{
if (methodReference.DeclaringType.FullName == _fullName)
instructions.Replace(i, RecreateMethodOperand(inst));
continue;
}
FieldReference fieldReference = inst.Operand as FieldReference;
if (fieldReference != null)
{
if (fieldReference.DeclaringType.FullName == _fullName)
instructions.Replace(i, RecreateFieldOperand(inst));
continue;
}
}
}
private Instruction RecreateMethodOperand(Instruction inst)
{
MethodDefinition method = _newDefination.GetMethod((MethodReference)inst.Operand);
MethodReference methodReference = _module.Import(method);
return Instruction.Create(inst.OpCode, methodReference);
}
private Instruction RecreateFieldOperand(Instruction inst)
{
String oldName = ((FieldReference)inst.Operand).Name;
FieldDefinition field = _newDefination.GetField(oldName);
FieldReference fieldReference = _module.Import(field);
return Instruction.Create(inst.OpCode, fieldReference);
}
private Instruction RecreateTypeOperand(Instruction inst)
{
return Instruction.Create(inst.OpCode, _newReference);
}
}
public static MethodDefinition GetMethod(this TypeDefinition td, MethodReference descriptor)
{
foreach (MethodDefinition method in td.Methods)
{
if (method.Name != descriptor.Name)
continue;
if (method.ReturnType.FullName != descriptor.ReturnType.FullName)
continue;
if (method.HasGenericParameters)
{
if (!descriptor.HasGenericParameters)
continue;
if (method.GenericParameters.Count != descriptor.GenericParameters.Count)
continue;
for (int i = 0; i < method.GenericParameters.Count; i++)
{
if (method.GenericParameters[i].FullName != descriptor.GenericParameters[i].FullName)
continue;
}
}
else if (descriptor.HasGenericParameters)
{
continue;
}
if (method.HasParameters)
{
if (!descriptor.HasParameters)
continue;
if (method.Parameters.Count != descriptor.Parameters.Count)
continue;
for (int i = 0; i < method.Parameters.Count; i++)
{
ParameterDefinition p1 = method.Parameters[i];
ParameterDefinition p2 = descriptor.Parameters[i];
if (p1.ParameterType.FullName != p2.ParameterType.FullName)
continue;
if (p1.Attributes != p2.Attributes)
continue;
}
}
else if (descriptor.HasParameters)
{
continue;
}
return method;
}
throw new InvalidOperationException("Sequence contains more than one matching element.");
}
Заключительная версия, которая заменяет ссылки на старый тип новыми.
Неполная (отсутствует поддержка исключений, атрибутов). Но в моём сценарии пока работает. Буду допиливать по мере необходимости.
---
Обновил — добавил поддержку массивов.
Неполная (отсутствует поддержка исключений, атрибутов). Но в моём сценарии пока работает. Буду допиливать по мере необходимости.
---
Обновил — добавил поддержку массивов.
public sealed class TypeReplacer
{
private readonly String _fullName;
private readonly TypeReference _newReference;
private readonly TypeDefinition _newDefination;
private readonly List<ArrayType> _newArrayReferences;
private readonly Dictionary<TypeReference, TypeDefinition> _arrayDefinitions;
private ModuleDefinition _module;
private MethodDefinition _method;
public TypeReplacer(String fullName, TypeReference newReference)
{
_fullName = fullName;
_newReference = newReference;
_newDefination = newReference.Resolve();
_newArrayReferences = new List<ArrayType>();
_arrayDefinitions = new Dictionary<TypeReference, TypeDefinition>();
}
public void Replace(ModuleDefinition module)
{
_module = module;
foreach (TypeDefinition type in _module.Types)
ProcessType(type);
}
private void ProcessType(TypeDefinition type)
{
if (type.FullName == _fullName)
return;
ProcessProperties(type);
ProcessFields(type);
ProcessMethods(type);
ProcessNestedTypes(type);
}
private void ProcessNestedTypes(TypeDefinition type)
{
if (!type.HasNestedTypes)
return;
foreach (TypeDefinition nested in type.NestedTypes)
ProcessType(nested);
}
private void ProcessProperties(TypeDefinition type)
{
foreach (PropertyDefinition p in type.Properties)
{
TypeReference newType;
if (TryGetNewType(p.PropertyType, out newType))
p.PropertyType = newType;
}
}
private void ProcessFields(TypeDefinition type)
{
foreach (FieldDefinition f in type.Fields)
{
TypeReference newType;
if (TryGetNewType(f.FieldType, out newType))
f.FieldType = newType;
}
}
private void ProcessMethods(TypeDefinition type)
{
foreach (MethodDefinition m in type.Methods)
{
_method = m;
ProcessMethod();
}
}
private void ProcessMethod()
{
TypeReference newType;
if (TryGetNewType(_method.ReturnType, out newType))
_method.ReturnType = newType;
ProcessMethodParameters();
ProcessMethodBody();
}
private void ProcessMethodParameters()
{
if (!_method.HasParameters)
return;
foreach (ParameterDefinition p in _method.Parameters)
{
TypeReference newType;
if (TryGetNewType(p.ParameterType, out newType))
p.ParameterType = newType;
}
}
private void ProcessMethodBody()
{
if (!_method.HasBody)
return;
ProcessMethodBodyVariables();
ProcessMethodBodyInstructions();
}
private void ProcessMethodBodyVariables()
{
if (!_method.Body.HasVariables)
return;
foreach (VariableDefinition v in _method.Body.Variables)
{
TypeReference newType;
if (TryGetNewType(v.VariableType, out newType))
v.VariableType = newType;
}
}
private void ProcessMethodBodyInstructions()
{
TypeReference newType;
MethodBody body = _method.Body;
Collection<Instruction> instructions = body.Instructions;
for (int i = 0; i < instructions.Count; i++)
{
Instruction inst = instructions[i];
switch (inst.OpCode.OperandType)
{
case OperandType.InlineField:
case OperandType.InlineMethod:
case OperandType.InlineTok:
case OperandType.InlineType:
break;
default:
continue;
}
IGenericInstance genericInstance = inst.Operand as IGenericInstance;
if (genericInstance != null && genericInstance.HasGenericArguments)
{
for (int g = 0; g < genericInstance.GenericArguments.Count; g++)
{
if (TryGetNewType(genericInstance.GenericArguments[g], out newType))
genericInstance.GenericArguments[g] = newType;
}
}
TypeReference typeReference = inst.Operand as TypeReference;
if (typeReference != null)
{
if (TryGetNewType(typeReference, out newType))
instructions.Replace(i, RecreateTypeOperand(inst, newType));
continue;
}
MethodReference methodReference = inst.Operand as MethodReference;
if (methodReference != null)
{
if (TryGetNewType(methodReference.DeclaringType, out newType))
instructions.Replace(i, RecreateMethodOperand(inst, newType));
continue;
}
FieldReference fieldReference = inst.Operand as FieldReference;
if (fieldReference != null)
{
if (TryGetNewType(fieldReference.DeclaringType, out newType))
instructions.Replace(i, RecreateFieldOperand(inst, newType));
continue;
}
}
}
private Instruction RecreateMethodOperand(Instruction inst, TypeReference newType)
{
MethodDefinition method = ResolveType(newType).GetMethod((MethodReference)inst.Operand);
MethodReference methodReference = _module.Import(method);
return Instruction.Create(inst.OpCode, methodReference);
}
private Instruction RecreateFieldOperand(Instruction inst, TypeReference newType)
{
String oldName = ((FieldReference)inst.Operand).Name;
FieldDefinition field = ResolveType(newType).GetField(oldName);
FieldReference fieldReference = _module.Import(field);
return Instruction.Create(inst.OpCode, fieldReference);
}
private Instruction RecreateTypeOperand(Instruction inst, TypeReference newType)
{
return Instruction.Create(inst.OpCode, newType);
}
private Boolean TryGetNewType(TypeReference oldType, out TypeReference newType)
{
Int32 rank = 0;
while (oldType.IsArray)
{
rank++;
oldType = oldType.GetElementType();
}
if (oldType.FullName == _fullName)
{
newType = rank > 0 ? GetArrayType(rank) : _newReference;
return true;
}
newType = null;
return false;
}
private TypeDefinition ResolveType(TypeReference type)
{
if (type == _newReference)
return _newDefination;
TypeDefinition definition;
if (!_arrayDefinitions.TryGetValue(type, out definition))
{
definition = type.Resolve();
_arrayDefinitions.Add(type, definition);
}
return definition;
}
private ArrayType GetArrayType(int rank)
{
for (int i = _newArrayReferences.Count; i <= rank; i++)
_newArrayReferences.Add(new ArrayType(_newReference, i + 1));
return _newArrayReferences[rank];
}
}
public static MethodDefinition GetMethod(this TypeDefinition td, MethodReference descriptor)
{
foreach (MethodDefinition method in td.Methods)
{
if (method.Name != descriptor.Name)
continue;
if (method.ReturnType.FullName != descriptor.ReturnType.FullName)
continue;
if (method.HasGenericParameters)
{
if (!descriptor.HasGenericParameters)
continue;
if (method.GenericParameters.Count != descriptor.GenericParameters.Count)
continue;
for (int i = 0; i < method.GenericParameters.Count; i++)
{
if (method.GenericParameters[i].FullName != descriptor.GenericParameters[i].FullName)
continue;
}
}
else if (descriptor.HasGenericParameters)
{
continue;
}
if (method.HasParameters)
{
if (!descriptor.HasParameters)
continue;
if (method.Parameters.Count != descriptor.Parameters.Count)
continue;
for (int i = 0; i < method.Parameters.Count; i++)
{
ParameterDefinition p1 = method.Parameters[i];
ParameterDefinition p2 = descriptor.Parameters[i];
if (p1.ParameterType.FullName != p2.ParameterType.FullName)
continue;
if (p1.Attributes != p2.Attributes)
continue;
}
}
else if (descriptor.HasParameters)
{
continue;
}
return method;
}
throw new InvalidOperationException("Sequence contains more than one matching element.");
}