Last active
September 26, 2019 20:31
-
-
Save Sergio0694/365e940799ad77ce0a241d97df7ca6f9 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
// Custom delegate that takes the two ref parameters we need | |
public delegate void DataLoader(object instance, ref object r0, ref byte r1); | |
/* The method that takes a delegate and the list of discovered | |
* fields for the closure type and its nested closure types, | |
* and builds our dynamic IL method that loads all the fields sequentially */ | |
public static DataLoader BuildDataLoader( | |
Delegate instance | |
IReadOnlyList<ClosureField> fields) | |
{ | |
// Prepare the info for our custom delegate | |
Type returnType = typeof(void); | |
Type[] parameterTypes = | |
{ | |
typeof(object), | |
typeof(object).MakeByRefType(), | |
typeof(byte).MakeByRefType() | |
}; | |
// Create a new dynamic method | |
Type ownerType = instance.GetType(); | |
DynamicMethod method = new DynamicMethod( | |
$"GetFor{ownerType.Name}", | |
returnType, | |
parameterTypes, | |
ownerType); | |
ILGenerator il = method.GetILGenerator(); | |
// Mapping of parent members to index of the relative local variable | |
var map = | |
fields | |
.Where(m => m.Parents.Count > 0) | |
.SelectMany(m => m.Parents) | |
.Distinct() | |
.Select((m, i) => (Member: m, Index: i)) | |
.ToDictionary(p => (object)p.Member, p => p.Index + 1); | |
object root = new object(); // Placeholder | |
map.Add(root, 0); | |
// Set of indices of the loaded parent members | |
HashSet<int> loaded = new HashSet<int>(new[] { 0 }); | |
// Loads a given member on the top of the execution stack | |
void LoadMember(ClosureField member) | |
{ | |
// Load the parent instance on the execution stack | |
int index = map[member.Parents.LastOrDefault() ?? root]; | |
if (loaded.Contains(index)) il.EmitLoadLocal(index); | |
else | |
{ | |
// Seek upwards to find the most in depth loaded parent | |
int i = member.Parents.Count - 1; | |
while (i > 0 && !loaded.Contains(map[member.Parents[i]])) i--; | |
// Load the local variables for all the parents of the current member | |
il.EmitLoadLocal(i); | |
for (; i < member.Parents.Count; i++) | |
{ | |
index = map[member.Parents[i]]; | |
il.Emit(OpCodes.Ldfld, member.Parents[i]); | |
il.EmitStoreLocal(index); | |
il.EmitLoadLocal(index); | |
loaded.Add(index); | |
} | |
} | |
// Finally load the field from the object on the stack | |
il.Emit(OpCodes.Ldfld, member.Info); | |
} | |
// Declare the local variables | |
il.DeclareLocal(instance.Method.DeclaringType); | |
foreach (FieldInfo field in map.OrderBy(p => p.Value).Skip(1).Select(p => p.Key)) | |
{ | |
il.DeclareLocal(field.FieldType); | |
} | |
// Cast the closure instance and assign it to the local variable | |
il.Emit(OpCodes.Ldarg_0); | |
il.Emit(OpCodes.Castclass, instance.Method.DeclaringType); | |
il.Emit(OpCodes.Stloc_0); | |
// Handle all the captured fields, both objects and value types | |
int | |
referenceOffset = 0, // Offset into the references array | |
byteOffset = 0; // Offset into the bytes array | |
foreach (ClosureField field in fields) | |
{ | |
if (field.Info.FieldType.IsValueType) | |
{ | |
il.Emit(OpCodes.Ldarg_2); // Load ref byte r1 | |
il.EmitAddOffset(byteOffset); // Offset the reference | |
byteOffset += Marshal.SizeOf(field.Info.FieldType); | |
} | |
else | |
{ | |
// Load the offset address into the resource buffers | |
il.Emit(OpCodes.Ldarg_1); // Load ref object r0 | |
if (referenceOffset > 0) | |
{ | |
int offset = Unsafe.SizeOf<object>() * referenceOffset; | |
il.EmitAddOffset(offset); | |
} | |
referenceOffset++; | |
} | |
/* Load the current member accordingly. | |
* When this method returns, the value of the current | |
* member will be at the top of the execution stack */ | |
LoadMember(field); | |
il.EmitStoreToAddress(field.Info.FieldType); | |
} | |
il.Emit(OpCodes.Ret); | |
// Create the proper delegate type for the method | |
return (DataLoader)method.CreateDelegate(typeof(DataLoader)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment