Created
March 25, 2019 18:43
-
-
Save steveisok/a04557c332dfaf6ba5415a6b426ec39d to your computer and use it in GitHub Desktop.
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.Reflection; | |
using System.Reflection.Emit; | |
namespace Crash | |
{ | |
class MainClass | |
{ | |
public static void Main(string[] args) | |
{ | |
MainClass m = new MainClass(); | |
m.RunTest(); | |
} | |
void RunTest() | |
{ | |
EventHandler handler; | |
MethodInfo info; | |
Delegate del = Delegate.CreateDelegate( | |
type: typeof(EventHandler), | |
target: this, | |
method: "Page_Load", | |
ignoreCase: true, | |
throwOnBindFailure: false); | |
info = del.Method; | |
IntPtr ptr = info.MethodHandle.GetFunctionPointer(); | |
handler = (new CalliEventHandlerDelegateProxy(this, ptr, false)).Handler; | |
handler(this, new EventArgs()); | |
} | |
void Page_Load(object sender, EventArgs e) | |
{ | |
Console.WriteLine("Page Load Called"); | |
} | |
} | |
// NOTE: This is lifted from refsrc System.Web as it is internal. This | |
// just helps shrink the test. | |
public class CalliEventHandlerDelegateProxy | |
{ | |
private delegate void ParameterlessDelegate(); | |
private delegate void ParameterfulDelegate(object sender, EventArgs e); | |
private IntPtr _functionPointer; | |
private Object _target; | |
private bool _argless; | |
public CalliEventHandlerDelegateProxy(Object target, IntPtr functionPointer, bool argless) | |
{ | |
_argless = argless; | |
_target = target; | |
_functionPointer = functionPointer; | |
} | |
public void Callback(Object sender, EventArgs e) | |
{ | |
if (_argless) | |
{ | |
ParameterlessDelegate del = FastDelegateCreator<ParameterlessDelegate>.BindTo(_target, _functionPointer); | |
del(); | |
} | |
else | |
{ | |
ParameterfulDelegate del = FastDelegateCreator<ParameterfulDelegate>.BindTo(_target, _functionPointer); | |
del(sender, e); | |
} | |
} | |
public EventHandler Handler | |
{ | |
get | |
{ | |
return new EventHandler(Callback); | |
} | |
} | |
} | |
public static class FastDelegateCreator<TDelegate> where TDelegate : class | |
{ | |
private static readonly Func<object, IntPtr, TDelegate> _factory = GetFactory(); | |
// obj is the 'this' pointer (potentially null) | |
// method is RuntimeMethodHandle.GetFunctionPointer() | |
public static TDelegate BindTo(object obj, IntPtr method) | |
{ | |
return _factory(obj, method); | |
} | |
// obj is the 'this' pointer (potentially null) | |
// method is the MethodInfo to bind to | |
public static TDelegate BindTo(object obj, MethodInfo method) | |
{ | |
return BindTo(obj, method.MethodHandle.GetFunctionPointer()); | |
} | |
// Assert needed since we'll be asked to create delegates to potentially private / protected methods | |
private static Func<object, IntPtr, TDelegate> GetFactory() | |
{ | |
// All delegates have a .ctor(object @object, IntPtr method), but since we can't call this | |
// directly from C# we just need to LCG it. | |
ConstructorInfo delegateCtor = typeof(TDelegate).GetConstructor(new Type[] { typeof(object), typeof(IntPtr) }); | |
// Define method FastCreateDelegate_X: (object, IntPtr) -> TDelegate | |
DynamicMethod dynamicMethod = new DynamicMethod( | |
name: "FastCreateDelegate_" + typeof(TDelegate).Name, | |
returnType: typeof(TDelegate), | |
parameterTypes: new Type[] { typeof(object), typeof(IntPtr) }, | |
owner: typeof(FastDelegateCreator<TDelegate>), | |
skipVisibility: true); | |
// return new TDelegate(obj, method); | |
ILGenerator ilGen = dynamicMethod.GetILGenerator(); | |
ilGen.Emit(OpCodes.Ldarg_0); // Stack contains ('this') | |
ilGen.Emit(OpCodes.Ldarg_1); // Stack contains ('this', 'method') | |
ilGen.Emit(OpCodes.Newobj, delegateCtor); // Stack contains (delegate) | |
ilGen.Emit(OpCodes.Ret); | |
return (Func<object, IntPtr, TDelegate>)dynamicMethod.CreateDelegate(typeof(Func<object, IntPtr, TDelegate>)); | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment