Skip to content

Instantly share code, notes, and snippets.

@dbr176
Last active February 26, 2019 17:10
Show Gist options
  • Save dbr176/5e8768ead9b9c8311b24c56d694876df to your computer and use it in GitHub Desktop.
Save dbr176/5e8768ead9b9c8311b24c56d694876df to your computer and use it in GitHub Desktop.
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
// --- Tricks for closures
void ReplaceMethod(Delegate d1, Delegate d2)
{
var ptr = d2.GetType()
.GetRuntimeFields()
.First(x => x.Name == "_methodPtr")
.GetValue(d2);
d1.GetType()
.GetRuntimeFields()
.First(x => x.Name == "_methodPtr")
.SetValue(d1, ptr);
}
var a = 0;
var b = 1;
var c = 2;
Func<int, int> f = x => x + a + b + c;
Func<int, int> g = x => x - a - b - c;
WriteLine("F: " + f(0));
WriteLine("G: " + g(0));
WriteLine();
// How to change variables captured by the closure
// Get closure object and cast it to dynamic
dynamic fd = f.Target;
// If we know the name of captured variable, we can change it
fd.a = -1;
fd.b = -1;
fd.c = -1;
WriteLine("F: " + f(0)); // Should be equal to -3 (0 - 1 - 1 - 1)
WriteLine("G: " + g(0));
WriteLine();
fd = f;
// How to change the method of the closure
ReplaceMethod(f, g);
WriteLine("F: " + f(0));
WriteLine("G: " + g(0));
WriteLine();
/* Output:
F: 3
G: -3
F: -3
G: 3
F: 3
G: 3
*/
// --- Unsafe cast
public interface IMethod
{
void Method();
}
public interface IMethod2
{
void Method();
}
public sealed class A : IMethod
{
// !!! Order of fields does matter
public int a = 666;
public int b = 777;
public void Method()
{
Console.WriteLine("I am A");
}
}
public sealed class B : IMethod2
{
// !!! a and b are swapped
public int b = -777;
public int a = -666;
public void Method()
{
Console.WriteLine("I am B");
}
}
var iamA = new A();
var iamB = new B();
var aCastToB = Unsafe.As<B>(iamA);
var bCastToA = Unsafe.As<A>(iamB);
// var aCastToMethod2 = (IMethod2)Unsafe.As<B>(iamA); - Exception :(
// var list = new List<IMethod2>();
// list.Add(aCastToB);
aCastToB.Method();
bCastToA.Method();
// B order of the fields is (b, a)
// A order of the fields is (a, b)
// So values of a and b in aCastToB are swapped
// a = 777, b = 666
Console.WriteLine($"a: {aCastToB.a}");
Console.WriteLine($"b: {aCastToB.b}");
Console.WriteLine($"a: {bCastToA.a}");
Console.WriteLine($"b: {bCastToA.b}");
/* Output:
I am B
I am A
a: 777
b: 666
a: -777
b: -666
*/
// How to cast IMethod to IMethod2?
var aPointer = *(void**)Unsafe.AsPointer(ref iamA);
var bPointer = *(void**)Unsafe.AsPointer(ref iamB);
var aMetaPointer = (void**)aPointer;
var bMetaPointer = (void**)bPointer;
*aMetaPointer = *bMetaPointer;
var aAsMethod2 = (IMethod2)(object)iamA;
iamA.Method();
var list = new List<IMethod2>();
list.Add(aAsMethod2);
list[0].Method();
/* Output:
I am B
I am B
*/
// How to break everything
var aAsMethod2AsA = Unsafe.As<A>((IMethod2)(object)iamA);
var aAsMethod2AsAAsMethod2 = (IMethod2)(object)Unsafe.As<A>((IMethod2)(object)iamA);
aAsMethod2AsA.Method();
aAsMethod2AsAAsMethod2.Method();
/* Output:
I am A
I am B
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment