Skip to content

Instantly share code, notes, and snippets.

@jbevain
Created May 5, 2010 15:08
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jbevain/390902 to your computer and use it in GitHub Desktop.
Save jbevain/390902 to your computer and use it in GitHub Desktop.
A rewriter to support methodof and fieldof operations
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