2010-12-07 4 views
3

Mono.Cecil 사용하기Mono.Cecil을 사용하여 System.Object.Equals에 호출을 주입하는 방법?

public string FirstName 
{ 
    get { return _FirstName; } 
    set 
    { 
     _FirstName = value; 
    } 
} 

다음으로 바꾸기 :

public string FirstName 
{ 
    get { return _FirstName; } 
    set 
    { 
     if (System.Object.Equals(_FirstName, value)) 
     { 
      return; 
     } 
     _FirstName = value; 
    } 
} 

이것은 재 작성 내용의 일부분이지만, 문제.

리플렉터 사용 다음 코드는 System.Object.Equals() 호출을 제외하고 필요한대로 속성을 다시 작성합니다. IL 코드가 다음과 같을 경우 :

하지만 다음과 같이 작성됩니다.

call instance void RewriteSharp.Person::.ctor() 

System.Object.Equals에 대한 호출을 작성하는 코드는 다음과 같습니다.

setMethodWriter.InsertBefore(
    firstExistingInstruction, 
    setMethodWriter.Create(OpCodes.Call, objectEqualsMethodReference)); 

ObjectEqualsMethodReference를 초기화하는 데 사용되는 메소드는 다음과 같습니다.

private static MethodReference GetSystemObjectEqualsMethodReference(
    AssemblyDefinition assembly 
) 
{ 

    var typeReference = assembly.MainModule.GetTypeReferences() 
     .Single(t => t.FullName == "System.Object"); 

    var typeDefinition = typeReference.Resolve(); 

    var methodDefinition = typeDefinition.Methods.Single(
          m => m.Name == "Equals" 
           && m.Parameters.Count == 2 
           && m.Parameters[0].ParameterType.Name == "Object" 
           && m.Parameters[1].ParameterType.Name == "Object" 
    ); 

    return methodDefinition; 
} 

setMethodWriter.Create() 또는 GetSystemObjectEqualsMethodReference()가 잘못되어 디버깅이 필요하지 않은 것으로 보입니다.

작성된 등록 정보와 등록 정보를 다시 쓰는 코드는 동일한 프레임 워크 목표를가집니다. 3.5 및 4.0 모두 실패합니다.

Mono.Cecil 빌드 master 분기 https://github.com/jbevain/cecil 사용하고 있습니다.

전체 코드 목록

using Mono.Cecil; 
using Mono.Cecil.Cil; 
using System; 
using System.Linq; 

namespace RewriteNotifyPropertyChanged 
{ 
class Program 
{ 
static void Main(string[] args) 
{ 
    var rewrite = "..\\RewriteSharp.dll"; 
    var rewritten = "..\\RewritenSharp.dll"; 

    var typeName = "Person"; 
    var propertyName = "FirstName"; 

    var assembly = AssemblyDefinition.ReadAssembly(rewrite); 
    var typeDefinition = assembly.MainModule.Types.Single(t => t.Name == typeName); 
    var propertyDefintion = typeDefinition.Properties 
     .Single(p => p.Name == propertyName); 

    var setMethodWriter = propertyDefintion.SetMethod.Body.GetILProcessor(); 
    var backingFieldReference = GetBackingFieldReference(typeDefinition, propertyName); 
    var objectEqualsMethodReference = GetSystemObjectEqualsMethodReference(assembly); 
    var firstExistingInstruction = setMethodWriter.Body.Instructions[0]; 

    setMethodWriter.InsertBefore(
     firstExistingInstruction, 
     setMethodWriter.Create(OpCodes.Ldarg_0)); 

    setMethodWriter.InsertBefore(
     firstExistingInstruction, 
     setMethodWriter.Create(OpCodes.Ldfld, backingFieldReference)); 

    setMethodWriter.InsertBefore(
     firstExistingInstruction, 
     setMethodWriter.Create(OpCodes.Ldarg_1)); 

    setMethodWriter.InsertBefore(
     firstExistingInstruction, 
     setMethodWriter.Create(OpCodes.Call, objectEqualsMethodReference)); 

    setMethodWriter.InsertBefore(
     firstExistingInstruction, 
     setMethodWriter.Create(OpCodes.Brfalse_S, firstExistingInstruction)); 

    setMethodWriter.InsertBefore(
     firstExistingInstruction, 
     setMethodWriter.Create(OpCodes.Ret)); 

    assembly.Write(rewritten, new WriterParameters { WriteSymbols = true }); 

    Console.WriteLine("Done."); 
    Console.ReadKey(); 
} 

private static MethodReference GetSystemObjectEqualsMethodReference(
    AssemblyDefinition assembly 
) 
{ 

    var typeReference = assembly.MainModule.GetTypeReferences() 
     .Single(t => t.FullName == "System.Object"); 

    var typeDefinition = typeReference.Resolve(); 

    var methodDefinition = typeDefinition.Methods.Single(
          m => m.Name == "Equals" 
           && m.Parameters.Count == 2 
           && m.Parameters[0].ParameterType.Name == "Object" 
           && m.Parameters[1].ParameterType.Name == "Object" 
    ); 

    return methodDefinition; 
} 

private static FieldReference GetBackingFieldReference(
    TypeDefinition typeDefinition, 
    string propertyName 
) 
{ 
    var fieldName = "_" + propertyName; 
    var fieldReference = typeDefinition.Fields.Single(f => f.Name == fieldName); 

    return fieldReference; 
} 
} 
} 
+2

이것은 당신이 설명하는 문제를 해결하지 않지만 그럼에도 불구하고 문제 - 당신은 ldarg_0 지침 중 하나를 꺼내해야합니다. – cdhowie

+0

나는 Reflector에서 2 ldarg_0 명령을 전에 봤다고 맹세 할 수있다 :-). 감사. –

답변

6

세실,하는 System.Reflection 달리 기준과 정의의 차이를 만들고, 그 모듈 당 범위가된다. 즉, 독자적으로 다른 모듈의 MethodDefinition을 사용할 수 없다는 뜻입니다. 그것에 대한 적절한 참조를 만들어야합니다. 이것은 Cecil 용어로 importing이라는 프로세스입니다.

가 교체 :

var objectEqualsMethodReference = GetSystemObjectEqualsMethodReference(assembly); 

의 :

var objectEqualsMethodReference = assembly.MainModule.Import (GetSystemObjectEqualsMethodReference(assembly)); 

그리고

구체적으로는, GetSystemObjectEqualsMethodReference corlib에 정의 된 메소드를 반환, 당신은 당신의 모듈에 대한 참조를 만들 필요가 IL을 고정시켜야 작동 할 수 있습니다.나는 그것을, 방법이야하면서 또한

:

private static MethodReference GetSystemObjectEqualsMethodReference(AssemblyDefinition assembly) 
{ 
    var @object = assembly.MainModule.TypeSystem.Object.Resolve(); 

    return @object.Methods.Single(
     m => m.Name == "Equals" 
      && m.Parameters.Count == 2 
      && m.Parameters[0].ParameterType.MetadataType == MetadataType.Object 
      && m.Parameters[1].ParameterType.MetadataType == MetadataType.Object); 
} 

그리고

assembly.Write(rewritten, new WriterParameters { WriteSymbols = true }); 

훨씬 이해가되지 않습니다 :

private static MethodReference GetSystemObjectEqualsMethodReference(AssemblyDefinition assembly) 
{ 
    var typeReference = assembly.MainModule.GetTypeReferences() 
     .Single(t => t.FullName == "System.Object"); 

    var typeDefinition = typeReference.Resolve(); 

    var methodDefinition = typeDefinition.Methods.Single(
          m => m.Name == "Equals" 
           && m.Parameters.Count == 2 
           && m.Parameters[0].ParameterType.Name == "Object" 
           && m.Parameters[1].ParameterType.Name == "Object" 
    ); 

    return methodDefinition; 
} 

더 나은로 기록 될 것이다 어셈블리를 읽을 때 new ReaderParameters { ReadSymbols = true }을 전달하지 않은 경우.

+0

완벽. 고맙습니다. 나는 그 길을 따라 수입을 시도했지만 분명히 잘못되었다. –

1

KindOfMagic codeplex 프로젝트를 살펴볼 수 있습니다.

거의 비슷하지만 조금 좋아졌으며 Object.Equals()를 호출하지 않지만 대상 유형에 정의 된 항등 연산자를 사용합니다.

http://kindofmagic.codeplex.com