Skip to content

Instantly share code, notes, and snippets.

@ashalkhakov
Created September 22, 2015 10:08
Show Gist options
  • Save ashalkhakov/b3e4a9c2593600c930d1 to your computer and use it in GitHub Desktop.
Save ashalkhakov/b3e4a9c2593600c930d1 to your computer and use it in GitHub Desktop.
// 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