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

时间:2016-06-06 18:59:38

标签: c# mono cil mono.cecil

我正在尝试从以下位置重写属性的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方法?

提前致谢

1 个答案:

答案 0 :(得分:3)

之前的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指令并使用不正确的指令版本),但我认为这将有助于你理解如何做到这一点。