Commit bc39863e authored by Marc Gravell's avatar Marc Gravell

Support operator-based conversions

parent 6802611b
......@@ -2322,6 +2322,14 @@ public static void SetTypeMap(Type type, ITypeMap map)
else
{
// not a direct match; need to tweak the unbox
MethodInfo op;
if ((op = GetOperator(dataType, nullUnderlyingType ?? unboxType)) != null)
{ // this is handy for things like decimal <===> double
il.Emit(OpCodes.Unbox_Any, dataType); // stack is now [target][target][data-typed-value]
il.Emit(OpCodes.Call, op); // stack is now [target][target][typed-value]
}
else
{
bool handled = true;
OpCode opCode = default(OpCode);
if (dataTypeCode == TypeCode.Decimal || unboxTypeCode == TypeCode.Decimal)
......@@ -2373,10 +2381,15 @@ public static void SetTypeMap(Type type, ITypeMap map)
}
else
{ // use flexible conversion
il.Emit(OpCodes.Ldtoken, Nullable.GetUnderlyingType(unboxType) ?? unboxType); // stack is now [target][target][value][member-type-token]
il.Emit(OpCodes.Ldtoken, nullUnderlyingType ?? unboxType); // stack is now [target][target][value][member-type-token]
il.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null); // stack is now [target][target][value][member-type]
il.EmitCall(OpCodes.Call, typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(Type) }), null); // stack is now [target][target][boxed-member-type-value]
il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value]
il.Emit(OpCodes.Unbox_Any, nullUnderlyingType ?? unboxType); // stack is now [target][target][typed-value]
}
}
if (nullUnderlyingType != null)
{
il.Emit(OpCodes.Newobj, unboxType.GetConstructor(new[] { nullUnderlyingType })); // stack is now [target][target][typed-value]
}
}
......@@ -2468,6 +2481,27 @@ public static void SetTypeMap(Type type, ITypeMap map)
return (Func<IDataReader, object>)dm.CreateDelegate(typeof(Func<IDataReader, object>));
}
static MethodInfo GetOperator(Type from, Type to)
{
if (to == null) return null;
MethodInfo[] fromMethods, toMethods;
return ResolveOperator(fromMethods = from.GetMethods(BindingFlags.Static | BindingFlags.Public), from, to, "op_Implicit")
?? ResolveOperator(toMethods = to.GetMethods(BindingFlags.Static | BindingFlags.Public), from, to, "op_Implicit")
?? ResolveOperator(fromMethods, from, to, "op_Explicit")
?? ResolveOperator(toMethods, from, to, "op_Explicit");
}
static MethodInfo ResolveOperator(MethodInfo[] methods, Type from, Type to, string name)
{
for (int i = 0; i < methods.Length; i++)
{
if (methods[i].Name != name || methods[i].ReturnType != to) continue;
var args = methods[i].GetParameters();
if (args.Length != 1 || args[0].ParameterType != from) continue;
return methods[i];
}
return null;
}
private static void LoadLocal(ILGenerator il, int index)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment