Created
May 5, 2010 15:08
-
-
Save jbevain/390902 to your computer and use it in GitHub Desktop.
A rewriter to support methodof and fieldof operations
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using Mono.Cecil; | |
using Mono.Cecil.Cil; | |
class MethodProcessor { | |
readonly MethodBody body; | |
readonly MethodDefinition method; | |
readonly ModuleDefinition module; | |
ILProcessor il; | |
VariableDefinition reflection_parameters; | |
public MethodProcessor (MethodDefinition method) | |
{ | |
this.method = method; | |
this.body = method.Body; | |
this.module = method.Module; | |
} | |
ILProcessor GetILProcessor () | |
{ | |
if (il != null) | |
return il; | |
return il = body.GetILProcessor (); | |
} | |
VariableDefinition GetReflectionParameters () | |
{ | |
if (reflection_parameters != null) | |
return reflection_parameters; | |
reflection_parameters = new VariableDefinition (module.Import (typeof (System.Reflection.ParameterInfo []))); | |
body.Variables.Add (reflection_parameters); | |
body.InitLocals = true; | |
var il = GetILProcessor (); | |
var instructions = new [] { | |
il.Create (OpCodes.Ldtoken, method), | |
il.Create (OpCodes.Call, ImportGetMethodFromHandle ()), | |
il.Create (OpCodes.Callvirt, module.Import (typeof (System.Reflection.MethodBase).GetMethod ("GetParameters"))), | |
il.Create (OpCodes.Stloc, reflection_parameters), | |
}; | |
il.InsertBefore (body.Instructions [0], instructions); | |
return reflection_parameters; | |
} | |
MethodReference ImportGetMethodFromHandle () | |
{ | |
return module.Import (typeof (System.Reflection.MethodBase).GetMethod ("GetMethodFromHandle", new [] { typeof (RuntimeMethodHandle) })); | |
} | |
MethodReference ImportGetFieldFromHandle () | |
{ | |
return module.Import (typeof (System.Reflection.FieldInfo).GetMethod ("GetFieldFromHandle", new [] { typeof (RuntimeFieldHandle) })); | |
} | |
TypeReference ImportMethodInfo () | |
{ | |
return method.Module.Import (typeof (System.Reflection.MethodInfo)); | |
} | |
public void Run () | |
{ | |
foreach (var instruction in body.Instructions.ToArray ()) { | |
MethodReference reference; | |
if (!IsMethodCall (instruction, out reference)) | |
continue; | |
if (!IsOfMethod (reference)) | |
continue; | |
switch (reference.DeclaringType.Name) { | |
case "Parameter": | |
ProcessParameter (instruction); | |
break; | |
case "Property": | |
ProcessProperty (instruction); | |
break; | |
case "Method": | |
ProcessMethod (instruction); | |
break; | |
case "Field": | |
ProcessField (instruction); | |
break; | |
case "Event": | |
ProcessEvent (instruction); | |
break; | |
} | |
} | |
} | |
static bool IsOfMethod (MethodReference reference) | |
{ | |
return reference.Name == "Of"; | |
} | |
static bool IsMethodCall (Instruction instruction, out MethodReference reference) | |
{ | |
reference = null; | |
if (instruction.OpCode.FlowControl != FlowControl.Call) | |
return false; | |
reference = instruction.Operand as MethodReference; | |
return reference != null; | |
} | |
void ProcessParameter (Instruction instruction) | |
{ | |
var load_arg = instruction.Previous; | |
var il = GetILProcessor (); | |
var instructions = new [] { | |
il.Create (OpCodes.Ldloc, GetReflectionParameters ()), | |
il.Create (OpCodes.Ldc_I4, GetParameter (load_arg, method).Index), | |
il.Create (OpCodes.Ldelem_Ref), | |
}; | |
il.InsertBefore (load_arg, instructions); | |
il.Remove (load_arg); | |
il.Remove (instruction); | |
} | |
static ParameterDefinition GetParameter (Instruction instruction, MethodDefinition method) | |
{ | |
int index; | |
switch (instruction.OpCode.Code) { | |
case Code.Ldarg: | |
return (ParameterDefinition) instruction.Operand; | |
case Code.Ldarg_0: | |
case Code.Ldarg_S: | |
index = 0; | |
break; | |
case Code.Ldarg_1: | |
index = 1; | |
break; | |
case Code.Ldarg_2: | |
index = 2; | |
break; | |
default: | |
throw new NotSupportedException (); | |
} | |
if (method.HasThis) | |
index--; | |
return method.Parameters [index]; | |
} | |
void ProcessProperty (Instruction instruction) | |
{ | |
var call_get = instruction.Previous; | |
var load_subject = call_get.Previous; | |
var getter = (MethodReference) call_get.Operand; | |
var call_property = (MethodReference) instruction.Operand; | |
var il = GetILProcessor (); | |
var instructions = new [] { | |
il.Create (OpCodes.Ldtoken, getter), | |
il.Create (OpCodes.Call, ImportGetMethodFromHandle ()), | |
il.Create (OpCodes.Castclass, ImportMethodInfo ()), | |
il.Create (OpCodes.Call, call_property.DeclaringType.Resolve ().Methods.First (m => m.Name == "Of" && m.Parameters.Count > 0 && m.Parameters [0].ParameterType.Name == "MethodInfo")), | |
}; | |
il.InsertBefore (load_subject, instructions); | |
il.Remove (load_subject); | |
il.Remove (call_get); | |
il.Remove (instruction); | |
} | |
void ProcessMethod (Instruction instruction) | |
{ | |
var new_delegate = instruction.Previous; | |
var ld_ftn = new_delegate.Previous; | |
var ld_this = ld_ftn.Previous; | |
var il = GetILProcessor (); | |
var instructions = new [] { | |
il.Create (OpCodes.Ldtoken, ld_ftn.Operand as MethodReference), | |
il.Create (OpCodes.Call, ImportGetMethodFromHandle ()), | |
il.Create (OpCodes.Castclass, ImportMethodInfo ()), | |
}; | |
il.InsertBefore (ld_this, instructions); | |
il.Remove (ld_this); | |
il.Remove (ld_ftn); | |
il.Remove (new_delegate); | |
il.Remove (instruction); | |
} | |
void ProcessField (Instruction instruction) | |
{ | |
var load_field = instruction.Previous; | |
var load_this = IsStatic (load_field.OpCode) ? load_field.Previous : null; | |
var il = GetILProcessor (); | |
var instructions = new [] { | |
il.Create (OpCodes.Ldtoken, load_field.Operand as FieldReference), | |
il.Create (OpCodes.Call, ImportGetFieldFromHandle ()), | |
}; | |
il.InsertBefore (load_this ?? load_field, instructions); | |
if (load_this != null) | |
il.Remove (load_this); | |
il.Remove (load_field); | |
il.Remove (instruction); | |
} | |
static bool IsStatic (OpCode code) | |
{ | |
return code == OpCodes.Ldsfld; | |
} | |
void ProcessEvent (Instruction instruction) | |
{ | |
var call_event = (MethodReference) instruction.Operand; | |
var load_field = instruction.Previous; | |
var load_this = IsStatic (load_field.OpCode) ? load_field.Previous : null; | |
var il = GetILProcessor (); | |
var instructions = new [] { | |
il.Create (OpCodes.Ldtoken, load_field.Operand as FieldReference), | |
il.Create (OpCodes.Call, ImportGetFieldFromHandle ()), | |
il.Create (OpCodes.Call, call_event.DeclaringType.Resolve ().Methods.First (m => m.Name == "Of" && m.Parameters.Count > 0 && m.Parameters [0].ParameterType.Name == "FieldInfo")), | |
}; | |
il.InsertBefore (load_this ?? load_field, instructions); | |
if (load_this != null) | |
il.Remove (load_this); | |
il.Remove (load_field); | |
il.Remove (instruction); | |
} | |
} | |
static class ILProcessorRocks { | |
public static void InsertBefore (this ILProcessor il, Instruction target, IEnumerable<Instruction> instructions) | |
{ | |
foreach (var instruction in instructions) | |
il.InsertBefore (target, instruction); | |
} | |
} | |
class Program { | |
static void Main (string [] args) | |
{ | |
var module = ModuleDefinition.ReadModule (args [0]); | |
foreach (var type in module.Types) { | |
foreach (var method in type.Methods) { | |
if (!method.HasBody) | |
continue; | |
var processor = new MethodProcessor (method); | |
processor.Run (); | |
} | |
} | |
module.Write (args [0]); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment