Created
September 22, 2015 10:08
-
-
Save ashalkhakov/b3e4a9c2593600c930d1 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
// License: BSD 3-clause | |
// Author: Artyom Shalkhakov | |
// Date: Sep 22, 2015 | |
// | |
// About: this is a POC of n-tuples and row types implemented | |
// using Reflection.Emit. The mutation interface (get/set methods) | |
// involves no boxing, but the caller has to supply the type parameter | |
// that matches the type of the value at the given position. Position | |
// is resolved using a jump table (TODO: does it translate to threaded | |
// code?). | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
using System.Threading; | |
using System.Reflection; | |
using System.Reflection.Emit; | |
namespace TupleTest | |
{ | |
public interface INativeRowType | |
{ | |
// this makes the row type resemble a mutable N-tuple type | |
int NumColumns(); | |
T GetColumn<T>(int index); | |
void SetColumn<T>(int index, T elt); | |
// purely for convenience: row operations | |
// string[] Columns(); | |
string ColumnName(int index); | |
int ColumnIndex(string columnName); | |
T GetColumn<T>(string name); | |
void SetColumn<T>(string name, T elt); | |
} | |
class Program | |
{ | |
static Type MakeRecordType(string typename, SortedDictionary<string, Type> rec) | |
{ | |
// create a dynamic assembly and module | |
AssemblyName assemblyName = new AssemblyName(); | |
assemblyName.Name = "tmpAssembly"; | |
AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); | |
ModuleBuilder module = assemblyBuilder.DefineDynamicModule("tmpModule"); | |
// create a new type builder | |
TypeBuilder typeBuilder = module.DefineType(typename, TypeAttributes.Public | TypeAttributes.Class); | |
// put labels into a static array | |
/* | |
var labels = typeBuilder.DefineField("_labels", typeof(string[]), FieldAttributes.Private | FieldAttributes.Static); | |
{ | |
var ctor = typeBuilder.DefineTypeInitializer(); | |
var ctor_I = ctor.GetILGenerator(); | |
var arr = ctor_I.DeclareLocal(typeof(string[])); | |
ctor_I.Emit(OpCodes.Ldc_I4, rec.Keys.Count); | |
ctor_I.Emit(OpCodes.Newarr, typeof(string)); | |
ctor_I.Emit(OpCodes.Stloc, arr); | |
var colIndex = 0; | |
foreach (var lab in rec.Keys) | |
{ | |
ctor_I.Emit(OpCodes.Ldloc, arr); | |
ctor_I.Emit(OpCodes.Ldc_I4, colIndex); | |
ctor_I.Emit(OpCodes.Ldstr, lab); | |
ctor_I.Emit(OpCodes.Stelem); | |
colIndex++; | |
} | |
ctor_I.Emit(OpCodes.Stsfld, labels); | |
} | |
*/ | |
SortedDictionary<string, FieldBuilder> fields = new SortedDictionary<string, FieldBuilder>(); | |
foreach (var lab in rec.Keys) | |
{ | |
// Generate a private field | |
FieldBuilder field = typeBuilder.DefineField("_" + lab, rec[lab], FieldAttributes.Private); | |
fields[lab] = field; | |
PropertyBuilder property = | |
typeBuilder.DefineProperty(lab, | |
PropertyAttributes.None, | |
rec[lab], | |
new Type[] { rec[lab] }); | |
MethodAttributes GetSetAttr = | |
MethodAttributes.Public | | |
MethodAttributes.HideBySig; | |
MethodBuilder currGetPropMthdBldr = | |
typeBuilder.DefineMethod("get_" + lab, | |
GetSetAttr, | |
rec[lab], | |
Type.EmptyTypes); | |
ILGenerator currGetIL = currGetPropMthdBldr.GetILGenerator(); | |
currGetIL.Emit(OpCodes.Ldarg_0); | |
currGetIL.Emit(OpCodes.Ldfld, field); | |
currGetIL.Emit(OpCodes.Ret); | |
// Define the "set" accessor method for current private field. | |
MethodBuilder currSetPropMthdBldr = | |
typeBuilder.DefineMethod("set_" + lab, | |
GetSetAttr, | |
null, | |
new Type[] { rec[lab] }); | |
ILGenerator currSetIL = currSetPropMthdBldr.GetILGenerator(); | |
currSetIL.Emit(OpCodes.Ldarg_0); | |
currSetIL.Emit(OpCodes.Ldarg_1); | |
currSetIL.Emit(OpCodes.Stfld, field); | |
currSetIL.Emit(OpCodes.Ret); | |
property.SetGetMethod(currGetPropMthdBldr); | |
property.SetSetMethod(currSetPropMthdBldr); | |
} | |
// Generate GetHashCode? | |
// NOTE: algorithm from https://www.jetbrains.com/resharper/documentation/help20/AdvEditing/generateEquals.html | |
// Generate IEquatable<T> | |
{ | |
var tp_equatable = typeof(IEquatable<>).MakeGenericType(typeBuilder); | |
typeBuilder.AddInterfaceImplementation(tp_equatable); | |
var Equals = typeBuilder.DefineMethod("Equals", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Final, typeof(Boolean), new[] { typeBuilder }); | |
var Equals_I = Equals.GetILGenerator(); | |
var Equals_L0 = Equals_I.DefineLabel(); | |
// compare all columns for equality one by one | |
foreach (var lab in rec.Keys) | |
{ | |
var tp = rec[lab]; | |
Equals_I.Emit(OpCodes.Ldarg_0); | |
Equals_I.Emit(OpCodes.Ldfld, fields[lab]); | |
Equals_I.Emit(OpCodes.Ldarg_1); | |
Equals_I.Emit(OpCodes.Ldfld, fields[lab]); | |
Equals_I.Emit(OpCodes.Ceq); | |
Equals_I.Emit(OpCodes.Brfalse, Equals_L0); | |
} | |
// the true case: | |
Equals_I.Emit(OpCodes.Ldc_I4_1); | |
Equals_I.Emit(OpCodes.Ret); | |
// the false case: | |
Equals_I.MarkLabel(Equals_L0); | |
Equals_I.Emit(OpCodes.Ldc_I4_0); | |
Equals_I.Emit(OpCodes.Ret); | |
} | |
// Generate IComparable<T> | |
{ | |
var tp_comparable = typeof(IComparable<>).MakeGenericType(typeBuilder); | |
typeBuilder.AddInterfaceImplementation(tp_comparable); | |
var CompareTo = typeBuilder.DefineMethod("CompareTo", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Final, typeof(Int32), new[] { typeBuilder }); | |
var CompareTo_I = CompareTo.GetILGenerator(); | |
var CompareTo_L0 = CompareTo_I.DefineLabel(); | |
var CompareTo_Tmp = CompareTo_I.DeclareLocal(typeof(int)); | |
// compare all columns one by one | |
foreach (var lab in rec.Keys) | |
{ | |
var tp = rec[lab]; | |
CompareTo_I.Emit(OpCodes.Ldarg_0); | |
CompareTo_I.Emit(OpCodes.Ldfld, fields[lab]); | |
CompareTo_I.Emit(OpCodes.Ldarg_1); | |
CompareTo_I.Emit(OpCodes.Ldfld, fields[lab]); | |
var mi = tp.GetMethod("CompareTo", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { tp }, null); | |
if (mi == null) | |
throw new Exception("CompareTo not implemented for type " + tp.FullName); | |
CompareTo_I.Emit(OpCodes.Call, mi); | |
CompareTo_I.Emit(OpCodes.Stloc, CompareTo_Tmp); | |
CompareTo_I.Emit(OpCodes.Ldloc, CompareTo_Tmp); | |
CompareTo_I.Emit(OpCodes.Brtrue, CompareTo_L0); | |
} | |
// the true case: | |
CompareTo_I.Emit(OpCodes.Ldc_I4_0); | |
CompareTo_I.Emit(OpCodes.Ret); | |
// the false case: | |
CompareTo_I.MarkLabel(CompareTo_L0); | |
CompareTo_I.Emit(OpCodes.Ldloc, CompareTo_Tmp); | |
CompareTo_I.Emit(OpCodes.Ret); | |
} | |
// Generate INativeRowType | |
typeBuilder.AddInterfaceImplementation(typeof(INativeRowType)); | |
// | |
{ | |
var NumColumn = typeBuilder.DefineMethod("NumColumns", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Final, typeof(Int32), Type.EmptyTypes); | |
var NumColumn_I = NumColumn.GetILGenerator(); | |
NumColumn_I.Emit(OpCodes.Ldc_I4, rec.Keys.Count); | |
NumColumn_I.Emit(OpCodes.Ret); | |
} | |
// | |
/* | |
{ | |
var Columns = typeBuilder.DefineMethod("Columns", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Final, typeof(string[]), Type.EmptyTypes); | |
var Columns_I = Columns.GetILGenerator(); | |
Columns_I.Emit(OpCodes.Ldsfld, labels); | |
Columns_I.Emit(OpCodes.Ret); | |
} | |
*/ | |
// | |
// TODO: for big record types, might use hash tables instead | |
{ | |
var ColumnName = typeBuilder.DefineMethod("ColumnName", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Final, typeof(string), new Type[] { typeof(int) }); | |
var ColumnName_I = ColumnName.GetILGenerator(); | |
var ColumnName_L = ColumnName_I.DefineLabel(); | |
var ColumnName_JT = new Label[rec.Keys.Count]; | |
for (var i = 0; i < rec.Keys.Count; i++) | |
ColumnName_JT[i] = ColumnName_I.DefineLabel(); | |
ColumnName_I.Emit(OpCodes.Ldarg_1); | |
ColumnName_I.Emit(OpCodes.Switch, ColumnName_JT); | |
ColumnName_I.Emit(OpCodes.Br, ColumnName_L); | |
var colIndex = 0; | |
foreach (var lab in rec.Keys) | |
{ | |
var tp = rec[lab]; | |
ColumnName_I.MarkLabel(ColumnName_JT[colIndex]); | |
ColumnName_I.Emit(OpCodes.Ldstr, lab); | |
ColumnName_I.Emit(OpCodes.Ret); | |
colIndex++; | |
} | |
ColumnName_I.MarkLabel(ColumnName_L); | |
ColumnName_I.Emit(OpCodes.Ldnull); | |
ColumnName_I.Emit(OpCodes.Ret); | |
} | |
// | |
var ColumnIndex = typeBuilder.DefineMethod("ColumnIndex", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Final, typeof(Int32), new Type[] { typeof(string) }); | |
{ | |
var ColumnIndex_I = ColumnIndex.GetILGenerator(); | |
var colIndex = 0; | |
foreach (var lab in rec.Keys) | |
{ | |
var tp = rec[lab]; | |
var ColumnIndex_L = ColumnIndex_I.DefineLabel(); | |
ColumnIndex_I.Emit(OpCodes.Ldstr, lab); | |
ColumnIndex_I.Emit(OpCodes.Ldarg_1); | |
ColumnIndex_I.Emit(OpCodes.Ceq); | |
ColumnIndex_I.Emit(OpCodes.Brfalse, ColumnIndex_L); | |
ColumnIndex_I.Emit(OpCodes.Ldc_I4, colIndex); | |
ColumnIndex_I.Emit(OpCodes.Ret); | |
ColumnIndex_I.MarkLabel(ColumnIndex_L); | |
colIndex++; | |
} | |
ColumnIndex_I.Emit(OpCodes.Ldnull); | |
ColumnIndex_I.Emit(OpCodes.Ret); | |
} | |
// | |
var GetColumnIndex = typeBuilder.DefineMethod("GetColumn", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Final); | |
{ | |
var GetColumnIndexTypeParameters = GetColumnIndex.DefineGenericParameters(new[] { "T" }); | |
var GetColumnIndexT = GetColumnIndexTypeParameters[0]; | |
GetColumnIndex.SetParameters(new Type[] { typeof(int) }); | |
GetColumnIndex.SetReturnType(GetColumnIndexT); | |
var GetColumnIndex_I = GetColumnIndex.GetILGenerator(); | |
var GetColumnIndex_JT = new Label[rec.Keys.Count]; | |
for (var i = 0; i < rec.Keys.Count; i++) | |
GetColumnIndex_JT[i] = GetColumnIndex_I.DefineLabel(); | |
var GetColumnIndex_L = GetColumnIndex_I.DefineLabel(); | |
GetColumnIndex_I.Emit(OpCodes.Ldarg_1); | |
GetColumnIndex_I.Emit(OpCodes.Switch, GetColumnIndex_JT); | |
GetColumnIndex_I.Emit(OpCodes.Br, GetColumnIndex_L); | |
var colIndex = 0; | |
foreach (var lab in rec.Keys) | |
{ | |
var tp = rec[lab]; | |
GetColumnIndex_I.MarkLabel(GetColumnIndex_JT[colIndex]); | |
// check the type | |
var continueLabel = GetColumnIndex_I.DefineLabel(); | |
GetColumnIndex_I.Emit(OpCodes.Ldtoken, GetColumnIndexT); | |
GetColumnIndex_I.Emit(OpCodes.Ldtoken, tp); | |
GetColumnIndex_I.Emit(OpCodes.Ceq); | |
GetColumnIndex_I.Emit(OpCodes.Brtrue_S, continueLabel); | |
GetColumnIndex_I.Emit(OpCodes.Ldstr, "The specified type is not supported by this operation."); | |
GetColumnIndex_I.Emit(OpCodes.Newobj, typeof(ArgumentException).GetConstructor(new[] { typeof(string) })); | |
GetColumnIndex_I.Emit(OpCodes.Throw); | |
GetColumnIndex_I.MarkLabel(continueLabel); | |
GetColumnIndex_I.Emit(OpCodes.Ldarg_0); | |
GetColumnIndex_I.Emit(OpCodes.Ldfld, fields[lab]); | |
GetColumnIndex_I.Emit(OpCodes.Ret); | |
colIndex++; | |
} | |
GetColumnIndex_I.MarkLabel(GetColumnIndex_L); | |
GetColumnIndex_I.Emit(OpCodes.Ldstr, "The specified column is not present."); | |
GetColumnIndex_I.Emit(OpCodes.Newobj, typeof(ArgumentException).GetConstructor(new[] { typeof(string) })); | |
GetColumnIndex_I.Emit(OpCodes.Throw); | |
} | |
// | |
{ | |
var GetColumnName = typeBuilder.DefineMethod("GetColumn", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Final); | |
var GetColumnTypeParameters = GetColumnName.DefineGenericParameters(new[] { "T" }); | |
var GetColumnT = GetColumnTypeParameters[0]; | |
GetColumnName.SetParameters(new Type[] { typeof(string) }); | |
GetColumnName.SetReturnType(GetColumnT); | |
var GetColumnName_I = GetColumnName.GetILGenerator(); | |
GetColumnName_I.Emit(OpCodes.Ldarg_0); | |
GetColumnName_I.Emit(OpCodes.Ldarg_0); | |
GetColumnName_I.Emit(OpCodes.Ldarg_1); | |
GetColumnName_I.Emit(OpCodes.Call, ColumnIndex); | |
GetColumnName_I.Emit(OpCodes.Call, GetColumnIndex); | |
GetColumnName_I.Emit(OpCodes.Ret); | |
} | |
// | |
var SetColumnIndex = typeBuilder.DefineMethod("SetColumn", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Final); | |
{ | |
var SetColumnIndexTypeParameters = SetColumnIndex.DefineGenericParameters(new[] { "T" }); | |
var SetColumnIndexT = SetColumnIndexTypeParameters[0]; | |
SetColumnIndex.SetParameters(new Type[] { typeof(int), SetColumnIndexT }); | |
SetColumnIndex.SetReturnType(typeof(void)); | |
var SetColumnIndex_I = SetColumnIndex.GetILGenerator(); | |
var SetColumnIndex_L = SetColumnIndex_I.DefineLabel(); | |
var SetColumnIndex_JT = new Label[rec.Keys.Count]; | |
for (var i = 0; i < rec.Keys.Count; i++) | |
SetColumnIndex_JT[i] = SetColumnIndex_I.DefineLabel(); | |
SetColumnIndex_I.Emit(OpCodes.Ldarg_1); | |
SetColumnIndex_I.Emit(OpCodes.Switch, SetColumnIndex_JT); | |
SetColumnIndex_I.Emit(OpCodes.Br, SetColumnIndex_L); | |
var colIndex = 0; | |
foreach (var lab in rec.Keys) | |
{ | |
var tp = rec[lab]; | |
SetColumnIndex_I.MarkLabel(SetColumnIndex_JT[colIndex]); | |
// check the type | |
// typeof(T).Equals(tp) | |
var continueLabel = SetColumnIndex_I.DefineLabel(); | |
SetColumnIndex_I.Emit(OpCodes.Ldtoken, SetColumnIndexT); | |
SetColumnIndex_I.Emit(OpCodes.Ldtoken, tp); | |
SetColumnIndex_I.Emit(OpCodes.Ceq); | |
SetColumnIndex_I.Emit(OpCodes.Brtrue_S, continueLabel); | |
SetColumnIndex_I.Emit(OpCodes.Ldstr, "The specified type is not supported by this operation."); | |
SetColumnIndex_I.Emit(OpCodes.Newobj, typeof(ArgumentException).GetConstructor(new[] { typeof(string) })); | |
SetColumnIndex_I.Emit(OpCodes.Throw); | |
SetColumnIndex_I.MarkLabel(continueLabel); | |
SetColumnIndex_I.Emit(OpCodes.Ldarg_0); | |
SetColumnIndex_I.Emit(OpCodes.Ldarg_2); | |
SetColumnIndex_I.Emit(OpCodes.Stfld, fields[lab]); | |
SetColumnIndex_I.Emit(OpCodes.Ret); | |
colIndex++; | |
} | |
SetColumnIndex_I.MarkLabel(SetColumnIndex_L); | |
SetColumnIndex_I.Emit(OpCodes.Ldstr, "The specified column is not present."); | |
SetColumnIndex_I.Emit(OpCodes.Newobj, typeof(ArgumentException).GetConstructor(new[] { typeof(string) })); | |
SetColumnIndex_I.Emit(OpCodes.Throw); | |
} | |
// | |
{ | |
var SetColumnName = typeBuilder.DefineMethod("SetColumn", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Final); | |
var SetColumnNameTypeParameters = SetColumnName.DefineGenericParameters(new[] { "T" }); | |
var SetColumnNameT = SetColumnNameTypeParameters[0]; | |
SetColumnName.SetParameters(new Type[] { typeof(string), SetColumnNameT }); | |
SetColumnName.SetReturnType(typeof(void)); | |
var SetColumnName_I = SetColumnName.GetILGenerator(); | |
SetColumnName_I.Emit(OpCodes.Ldarg_0); | |
SetColumnName_I.Emit(OpCodes.Ldarg_0); | |
SetColumnName_I.Emit(OpCodes.Ldarg_1); | |
SetColumnName_I.Emit(OpCodes.Call, ColumnIndex); | |
SetColumnName_I.Emit(OpCodes.Ldarg_2); | |
SetColumnName_I.Emit(OpCodes.Call, SetColumnIndex); | |
SetColumnName_I.Emit(OpCodes.Ret); | |
} | |
// | |
// Generate our type | |
Type generatedType = typeBuilder.CreateType(); | |
return generatedType; | |
} | |
static void UseType(SortedDictionary<string,Type> rec, Type nativeType) | |
{ | |
// one generic operation is printing | |
// - what are other useful row-polymorphic operations? | |
// - project? rename? extend? | |
// check for structural equality? | |
// comparison (lt/eq/ge)? | |
// - a < b iff all columns of a are pairwise < than corresp. columns of b | |
object generatedObject = Activator.CreateInstance(nativeType); | |
object generatedObject2 = Activator.CreateInstance(nativeType); | |
// convert to object array? array length = column count | |
// initialize from an object array? array length = column count | |
{ | |
DynamicMethod initer = new DynamicMethod("Initer", null, new Type[] { nativeType, typeof(object[]) }, nativeType.Module); | |
ILGenerator il = initer.GetILGenerator(); | |
int i = 0; | |
foreach (var lab in rec.Keys) { | |
var tp = rec[lab]; | |
il.Emit(OpCodes.Ldarg_0); | |
il.Emit(OpCodes.Ldarg_1); | |
il.Emit(OpCodes.Ldc_I4, i); | |
il.Emit(OpCodes.Ldelem_Ref); | |
// some types should not be unboxed (ref types should be cast!) | |
il.Emit(OpCodes.Unbox_Any, tp); | |
var mi = nativeType.GetProperty(lab).GetSetMethod(); | |
il.Emit(OpCodes.Call, mi); | |
i++; | |
} | |
il.Emit(OpCodes.Ret); | |
var init = (Action<object[]>)initer.CreateDelegate(typeof(Action<>).MakeGenericType(typeof(object[])), generatedObject); | |
init(new[]{ (object)1, (object)"hey" }); | |
} | |
((INativeRowType)generatedObject2).SetColumn<int>(0, 3); | |
((INativeRowType)generatedObject2).SetColumn<string>(1, "hello again"); | |
// test comparable/equatable | |
var eqt = typeof(IEquatable<>).MakeGenericType(nativeType).GetMethod("Equals", BindingFlags.Public | BindingFlags.Instance); | |
Console.WriteLine("equatable(refl): {0}", eqt.Invoke(generatedObject, new object[] { generatedObject })); | |
Console.WriteLine("equatable(diff): {0}", eqt.Invoke(generatedObject, new object[] { generatedObject2 })); | |
// NRE here... why? | |
// var comp = nativeType.GetMethod("CompareTo", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { generatedObject.GetType() }, null); | |
// Console.WriteLine("comparable: {0}", comp.Invoke(generatedObject, new object[] { generatedObject2 })); | |
// test INativeRowType | |
Console.WriteLine("NumColumns: {0}", ((INativeRowType)generatedObject).NumColumns()); | |
//Console.WriteLine("Columns: {0}", ((INativeRowType)generatedObject).Columns()); | |
Console.WriteLine("ColumnName(0) = {0}, ColumnName(1) = {1}", ((INativeRowType)generatedObject).ColumnName(0), ((INativeRowType)generatedObject).ColumnName(1)); | |
Console.WriteLine("ColumnIndex('X') = {0}, ColumnIndex('Y') = {1}", ((INativeRowType)generatedObject).ColumnIndex("X"), ((INativeRowType)generatedObject).ColumnIndex("Y")); | |
Console.WriteLine("GetColumn('X') = {0}, GetColumn('Y') = {1}", ((INativeRowType)generatedObject).GetColumn<int>("X"), ((INativeRowType)generatedObject).GetColumn<string>("Y")); | |
Console.WriteLine("GetColumn(0) = {0}, GetColumn(1) = {1}", ((INativeRowType)generatedObject).GetColumn<int>(0), ((INativeRowType)generatedObject).GetColumn<string>(1)); | |
((INativeRowType)generatedObject).SetColumn<int>("X", 2); | |
((INativeRowType)generatedObject).SetColumn<string>("Y", "hello"); | |
Console.WriteLine("after setting: GetColumn('X') = {0}, GetColumn('Y') = {1}", ((INativeRowType)generatedObject).GetColumn<int>("X"), ((INativeRowType)generatedObject).GetColumn<string>("Y")); | |
((INativeRowType)generatedObject).SetColumn<int>(0, 20); | |
((INativeRowType)generatedObject).SetColumn<string>(1, "hello there"); | |
Console.WriteLine("after setting: GetColumn(0) = {0}, GetColumn(1) = {1}", ((INativeRowType)generatedObject).GetColumn<int>(0), ((INativeRowType)generatedObject).GetColumn<string>(1)); | |
{ | |
// generate a method that, | |
// given a record, prints all the columns | |
DynamicMethod printer = new DynamicMethod("Printer", null, new Type[] { nativeType }, nativeType.Module); | |
ILGenerator ilGenerator = printer.GetILGenerator(); | |
foreach (var lab in rec.Keys) | |
{ | |
var tp = rec[lab]; | |
ilGenerator.Emit(OpCodes.Ldstr, lab + ": "); | |
ilGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("Write", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(string) }, null)); | |
ilGenerator.Emit(OpCodes.Ldarg_0); | |
var mi = nativeType.GetProperty(lab).GetGetMethod(); | |
ilGenerator.Emit(OpCodes.Call, mi); | |
ilGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("Write", BindingFlags.Static | BindingFlags.Public, null, new Type[] { tp }, null)); | |
ilGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null)); | |
} | |
// emit ret | |
ilGenerator.Emit(OpCodes.Ret); | |
var callback = (Action)printer.CreateDelegate(typeof(Action), generatedObject); | |
callback(); | |
} | |
} | |
static void UseType1(SortedDictionary<string, Type> rec, Type nativeType) | |
{ | |
var obj1 = Activator.CreateInstance(nativeType); | |
var obj2 = Activator.CreateInstance(nativeType); | |
((INativeRowType)obj1).SetColumn<float>(0, 2.5f); | |
((INativeRowType)obj2).SetColumn<float>(0, 2.6f); | |
//Console.WriteLine("Columns: {0}", ((INativeRowType)obj1).Columns()); | |
var eqt = typeof(IEquatable<>).MakeGenericType(nativeType).GetMethod("Equals", BindingFlags.Public | BindingFlags.Instance); | |
Console.WriteLine("equatable(refl): {0}", eqt.Invoke(obj1, new object[] { obj1 })); | |
Console.WriteLine("equatable(diff): {0}", eqt.Invoke(obj1, new object[] { obj2 })); | |
// FIXME: NRE here | |
//var cmp = typeof(IComparable<>).MakeGenericType(nativeType).GetMethod("CompareTo", BindingFlags.Public | BindingFlags.Instance); | |
//Console.WriteLine("comparable(refl): {0}", cmp.Invoke(obj1, new object[] { obj2 })); | |
//Console.WriteLine("comparable(diff): {0}", cmp.Invoke(obj1, new object[] { obj2 })); | |
} | |
static void BoxingTest(Type nativeType) | |
{ | |
// values: | |
// 1000000000: immediate OoM | |
// 100000000: dies after repeatedly failing to GC during initialization | |
// 10000000: very slow | |
// - memory utilization seems to be constant! so, the trick seems to work | |
const int numArr = 1000000; | |
var arr = Array.CreateInstance(nativeType, numArr); | |
for (var i = 0; i < numArr; i++) { | |
var obj = Activator.CreateInstance(nativeType); | |
((INativeRowType)obj).SetColumn<int>(0, i); | |
((INativeRowType)obj).SetColumn<string>(1, i.ToString()); | |
arr.SetValue(obj, i); | |
} | |
for (var i = 0; i < numArr; i++) | |
{ | |
Console.WriteLine(((INativeRowType)arr.GetValue(i)).GetColumn<int>(0)); | |
Console.WriteLine(((INativeRowType)arr.GetValue(i)).GetColumn<string>(1)); | |
} | |
} | |
static void Main(string[] args) | |
{ | |
var dict = new SortedDictionary<string, Type>(); | |
dict.Add("X", typeof(int)); | |
dict.Add("Y", typeof(string)); | |
var rowType = MakeRecordType("RowType1", dict); | |
UseType(dict, rowType); | |
var dict1 = new SortedDictionary<string, Type>(); | |
dict1.Add("A", typeof(float)); | |
var rowType1 = MakeRecordType("RowType2", dict1); | |
UseType1(dict1, rowType1); | |
BoxingTest(rowType); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment