views:

204

answers:

0

I'm trying to inject getter property into already compiled assembly.
Without backing field, it's reasonably easy:

AssemblyDefinition targetasmdef = AssemblyFactory.GetAssembly(@"E:\tmp\sandbox\Cecil\ConsoleApplication1\library\bin\Debug\library.dll");
TypeReference returnTypeRef = targetasmdef.MainModule.Import(typeof(string));
MethodDefinition getCity = new MethodDefinition("get_City", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, returnTypeRef);  

getCity.Body.CilWorker.Append(getCity.Body.CilWorker.Create(OpCodes.Nop));
getCity.Body.CilWorker.Append(getCity.Body.CilWorker.Create(OpCodes.Ldstr, "Warsaw"));
getCity.Body.CilWorker.Append(getCity.Body.CilWorker.Create(OpCodes.Stloc_0));
getCity.Body.CilWorker.Append(getCity.Body.CilWorker.Create(OpCodes.Ldloc_0));
getCity.Body.CilWorker.Append(getCity.Body.CilWorker.Create(OpCodes.Ret));  

getCity.SemanticsAttributes = MethodSemanticsAttributes.Getter;
targetasmdef.MainModule.Types[1].Methods.Add(getCity);
PropertyDefinition City = new PropertyDefinition("City", returnTypeRef, 0) { GetMethod = getCity };
 targetasmdef.MainModule.Types[1].Properties.Add(City);
 AssemblyFactory.SaveAssembly(targetasmdef, @"E:\tmp\sandbox\Cecil\ConsoleApplication1\library\bin\Debug\library_modified.dll");

Now my question is: Is it possible to create read only property, but with backing field (and set the value of the backing field)?

EDIT:
I've found a solution. Basically, necessary steps are:

  • Create backing field, see this post for reference
  • Create property, as in above code
  • Modify constructor body to assign value to property, eg.

    var targetasmdef = AssemblyFactory.GetAssembly(@"E:\tmp\library.dll");  
    var instructions = 
      targetasmdef.MainModule.Types[1].Constructors[0].Body.Instructions;  
    var stringInstructions = 
            (from Instruction Instr in instructions  
            where Instr.OpCode == OpCodes.Ldstr && Instr.Operand != null  
            select new  
            {  
                   Instr,  
                   Field = instructions[instructions.IndexOf(Instr) + 1]  
            }  
            ).ToList();  
    
    
    foreach (var item in stringInstructions)  
    {  
      Console.WriteLine(string.Format("Load string {0} to field {1}",
                                      item.Instr.Operand, item.Field.Operand));  
      if (item.Field.Operand.ToString().EndsWith("::_name"))  
        item.Instr.Operand = "newName";  
    }  
    AssemblyFactory.SaveAssembly(targetasmdef, @"E:\tmp\library_modified.dll");