Mono.Cecil:创建局部变量并更改return语句

Mono.Cecil: create local variable and change return statement

我正在尝试重写 属性 来自:

的 get 方法
 get
 {
    return dataString;
 }

至:

 get
 {
    string temp = dataString;
    PropertyLogging.Get("DataString", ref temp);
    return temp;
 }

到目前为止,我尝试了不同的方法:

//the static method I#m trying to insert
var getMethod = att.AttributeType.Resolve().Methods.FirstOrDefault(x => x.Name == _getMethodName);
if (getMethod != null)
{
    // ilProcessor.InsertBefore(returnInstruction, ilProcessor.Create(OpCodes.Starg, 1));

    ilProcessor.InsertBefore(returnInstruction, ilProcessor.CreateLoadInstruction(property.Name));

    // ilProcessor.InsertBefore(returnInstruction, ilProcessor.Create(OpCodes.Ldloc, 0));
    ilProcessor.InsertBefore(returnInstruction, ilProcessor.Create(OpCodes.Ldloca_S,  0));

    ilProcessor.InsertBefore(returnInstruction, ilProcessor.Create(OpCodes.Call, currentMethod.Module.ImportReference(getMethod)));

}

但它总是以这样的代码结尾(用 ilspy 反编译):

get
{
    string text = this.dataString;
    string arg_17_0 = text;
    string text2;
    PropertyLogging.Get("DataString", ref text2);
    return arg_17_0;
}

我目前的IL代码是:

IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  ldfld       UserQuery.dataString
IL_0007:  stloc.0     // text
IL_0008:  ldloc.0     // text
IL_0009:  stloc.1     // arg_17_0
IL_000A:  ldnull      
IL_000B:  stloc.2     // text2
IL_000C:  ldstr       "DataString"
IL_0011:  ldloca.s    02 // text2
IL_0013:  call        UserQuery+PropertyLogging.Get
IL_0018:  nop         
IL_0019:  ldloc.1     // arg_17_0
IL_001A:  stloc.3     
IL_001B:  br.s        IL_001D
IL_001D:  ldloc.3     
IL_001E:  ret    

但我需要的是:

IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  ldfld       UserQuery.dataString
IL_0007:  stloc.0     // temp
IL_0008:  ldstr       "DataString"
IL_000D:  ldloca.s    00 // temp
IL_000F:  call        UserQuery+PropertyLogging.Get
IL_0014:  nop         
IL_0015:  ldloc.0     // temp
IL_0016:  stloc.1     
IL_0017:  br.s        IL_0019
IL_0019:  ldloc.1     
IL_001A:  ret 

我也尝试过在方法主体中创建一个新变量,但我不知道如何使用它。

有谁知道如何正确重写这个get方法?

提前致谢

之前的 C# 代码:

public string DataString
{
   get { return _dataString; }
}

之前的 IL 代码:

.method public hidebysig specialname 
instance string get_DataString () cil managed 
{
// Method begins at RVA 0x2050
// Code size 12 (0xc)
.maxstack 1
.locals init (
    [0] string
)

    IL_0000: nop
    IL_0001: ldarg.0
    IL_0002: ldfld string ClassLibrary1.Class1::_dataString
    IL_0007: stloc.0
    IL_0008: br.s IL_000a
    IL_000a: ldloc.0
    IL_000b: ret
} // end of method Class1::get_DataString

重写:

public void InterceptPropertyGetter()
{
    // get the module where the property exist
    var module = ModuleDefinition.ReadModule(@"C:\temp\ClassLibrary1.dll");

    // get the property get method
    TypeDefinition myType = module.Types.First(type => type.Name == "Class1");
    var property = 
        myType.Properties.First(prop => prop.Name == "DataString").GetMethod;

    // get the _dataString field
    FieldDefinition dataStringDef = 
        myType.Fields.First(field => field.Name == "_dataString");
    FieldReference dataStringRef = module.Import(dataStringDef);

    // get the PropertyLogging static method
    MethodDefinition propertyLoggingDef = 
        myType.Methods.First(method => method.Name == "PropertyLogging");
    MethodReference propertyLoggingRef = module.Import(propertyLoggingDef);

    // clear the method (variables and instructions )
    property.Body.Variables.Clear();
    property.Body.Instructions.Clear();

    // define and init the locals
    property.Body.InitLocals = true;
    var tempVar = new VariableDefinition("temp", module.TypeSystem.String);
    property.Body.Variables.Add(tempVar);

    // write the IL
    var processor = property.Body.GetILProcessor();
    processor.Emit(OpCodes.Ldarg_0);
    processor.Emit(OpCodes.Ldfld, dataStringRef);
    processor.Emit(OpCodes.Stloc_0);
    processor.Emit(OpCodes.Ldstr, "DataString");
    processor.Emit(OpCodes.Ldloca_S, tempVar);
    processor.Emit(OpCodes.Call, propertyLoggingRef);
    processor.Emit(OpCodes.Ldloca_S, tempVar);
    processor.Emit(OpCodes.Ret);
    processor.Body.OptimizeMacros();

    // save the new module
    module.Write(@"C:\temp\ClassLibrary1_new.dll");
}

之后的 C# 代码:

public string DataString
{
    get
    {
        string dataString = this._dataString;
        Class1.PropertyLogging("DataString", ref dataString);
        return dataString;
    }
}

之后的 IL 代码:

.method public hidebysig specialname 
instance string get_DataString () cil managed 
{
  // Method begins at RVA 0x2050
  // Code size 22 (0x16)
  .maxstack 2
  .locals init (
   [0] string
  )
    IL_0000: ldarg.0
    IL_0001: ldfld string ClassLibrary1.Class1::_dataString
    IL_0006: stloc.0
    IL_0007: ldstr "DataString"
    IL_000c: ldloca.s 0
    IL_000e: call void ClassLibrary1.Class1::PropertyLogging(string, string&)
    IL_0013: ldloca.s 0
    IL_0015: ret
} // end of method Class1::get_DataString

使用 ILSpy 反编译。

这不是唯一的方法(例如,您不必全部清除,您可以添加 nop 指令并使用指令的非短版本),但我认为它会帮助您理解如何做到这一点。