Last active
February 26, 2019 17:10
-
-
Save dbr176/5e8768ead9b9c8311b24c56d694876df 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.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