I'm trying to rewrite the get method of an property from:
get
{
return dataString;
}
to:
get
{
string temp = dataString;
PropertyLogging.Get("DataString", ref temp);
return temp;
}
So far I've tried differnt things:
//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)));
}
But it always ends in code like (decompiled with ilspy):
get
{
string text = this.dataString;
string arg_17_0 = text;
string text2;
PropertyLogging.Get("DataString", ref text2);
return arg_17_0;
}
The IL code i currently have is:
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
But what I need is:
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
I've also tried ti create a new variable in the method body, but I don't have a clue how to use it.
Does anyone know how to correctly rewrite this get method?
thanks in advance
C# code before:
public string DataString
{
get { return _dataString; }
}
IL code before:
.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
Rewrite:
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# code after:
public string DataString
{
get
{
string dataString = this._dataString;
Class1.PropertyLogging("DataString", ref dataString);
return dataString;
}
}
IL code after:
.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
Decompile made with ILSpy.
This is not the only way (for example you don't must to clear all and you can add nop instructions and use unshort version of the instructions) but I think it will help you to understand how to do that.