Skip to content

Commit

Permalink
Merge branch 'fsharp' of https://github.com/vosen/dapper-dot-net into…
Browse files Browse the repository at this point in the history
… vosen-fsharp

Conflicts:
	Dapper/SqlMapper.cs
  • Loading branch information
mgravell committed Aug 7, 2012
2 parents e39c437 + 3151c6a commit 63d3747
Show file tree
Hide file tree
Showing 2 changed files with 348 additions and 46 deletions.
191 changes: 156 additions & 35 deletions Dapper/SqlMapper.cs
Expand Up @@ -1573,7 +1573,6 @@ static List<FieldInfo> GetSettableFields(Type t)
var il = dm.GetILGenerator();
il.DeclareLocal(typeof(int));
il.DeclareLocal(type);
bool haveEnumLocal = false;
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Stloc_0);
var properties = GetSettableProps(type);
Expand All @@ -1595,50 +1594,95 @@ static List<FieldInfo> GetSettableFields(Type t)
names.Add(reader.GetName(i));
}

var setters = (
from n in names
let prop = properties.FirstOrDefault(p => string.Equals(p.Name, n, StringComparison.Ordinal)) // property case sensitive first
?? properties.FirstOrDefault(p => string.Equals(p.Name, n, StringComparison.OrdinalIgnoreCase)) // property case insensitive second
let field = prop != null ? null : (fields.FirstOrDefault(p => string.Equals(p.Name, n, StringComparison.Ordinal)) // field case sensitive third
?? fields.FirstOrDefault(p => string.Equals(p.Name, n, StringComparison.OrdinalIgnoreCase))) // field case insensitive fourth
select new { Name = n, Property = prop, Field = field }
).ToList();

int index = startBound;

ConstructorInfo specializedConstructor = null;
ParameterInfo[] specializedParameters = null;
if (type.IsValueType)
{
il.Emit(OpCodes.Ldloca_S, (byte)1);
il.Emit(OpCodes.Initobj, type);
}
else
{
var ctor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
if (ctor == null)
var types = new Type[length];
for (int i = startBound; i < startBound + length; i++)
{
types[i - startBound] = reader.GetFieldType(i);
}
var constructors = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
bool hasDefaultConstructor = false;
foreach (ConstructorInfo ctor in constructors.OrderBy(c => c.IsPublic ? 0 : (c.IsPrivate ? 2 : 1)).ThenBy(c => c.GetParameters().Length))
{
ParameterInfo[] ctorParameters = ctor.GetParameters();
if (ctorParameters.Length == 0)
{
il.Emit(OpCodes.Newobj, ctor);
il.Emit(OpCodes.Stloc_1);
hasDefaultConstructor = true;
break;
}

if (ctorParameters.Length != types.Length)
continue;
int i = 0;
for (; i < ctorParameters.Length; i++)
{
if (!String.Equals(ctorParameters[i].Name, names[i], StringComparison.OrdinalIgnoreCase))
break;
if (types[i] == typeof(byte[]) && ctorParameters[i].ParameterType.FullName == LinqBinary)
continue;
var unboxedType = Nullable.GetUnderlyingType(ctorParameters[i].ParameterType) ?? ctorParameters[i].ParameterType;
if (unboxedType != types[i]
&& !(unboxedType.IsEnum && Enum.GetUnderlyingType(unboxedType) == types[i])
&& !(unboxedType == typeof(char) && types[i] == typeof(string)))
break;
}
if (i == ctorParameters.Length)
{
specializedConstructor = ctor;
specializedParameters = ctorParameters;
break;
}
}
if (!hasDefaultConstructor && specializedConstructor == null)
{
throw new InvalidOperationException("A parameterless default constructor is required to allow for dapper materialization");
string proposedTypes = "(" + String.Join(", ", types.Select((t, i) => t.FullName + " " + names[i]).ToArray()) + ")";
throw new InvalidOperationException(String.Format("A parameterless default constructor or one matching signature {0} is required for {1} materialization", proposedTypes, type.FullName));
}
il.Emit(OpCodes.Newobj, ctor);
il.Emit(OpCodes.Stloc_1);
}
il.BeginExceptionBlock();
if(type.IsValueType)
{
il.Emit(OpCodes.Ldloca_S, (byte)1);// [target]
} else
}
else if(specializedConstructor == null)
{
il.Emit(OpCodes.Ldloc_1);// [target]
}

var setters = specializedConstructor != null ?
names.Select((n, i) => new { Name = n, Property = new PropInfo() { Type = specializedParameters[i].ParameterType }, Field = (FieldInfo)null }).ToList()
:
(from n in names
let prop = properties.FirstOrDefault(p => string.Equals(p.Name, n, StringComparison.Ordinal)) // property case sensitive first
?? properties.FirstOrDefault(p => string.Equals(p.Name, n, StringComparison.OrdinalIgnoreCase)) // property case insensitive second
let field = prop != null ? null : (fields.FirstOrDefault(p => string.Equals(p.Name, n, StringComparison.Ordinal)) // field case sensitive third
?? fields.FirstOrDefault(p => string.Equals(p.Name, n, StringComparison.OrdinalIgnoreCase))) // field case insensitive fourth
select new { Name = n, Property = prop, Field = field }
).ToList();

// stack is now [target]

bool first = true;
var allDone = il.DefineLabel();
int enumDeclareLocal = -1;
foreach (var item in setters)
{
if (item.Property != null || item.Field != null)
{
il.Emit(OpCodes.Dup); // stack is now [target][target]
if(specializedConstructor == null)
il.Emit(OpCodes.Dup); // stack is now [target][target]
Label isDbNullLabel = il.DefineLabel();
Label finishLabel = il.DefineLabel();

Expand Down Expand Up @@ -1668,17 +1712,16 @@ static List<FieldInfo> GetSettableFields(Type t)

if (unboxType.IsEnum)
{
if (!haveEnumLocal)
if (enumDeclareLocal == -1)
{
il.DeclareLocal(typeof(string));
haveEnumLocal = true;
enumDeclareLocal = il.DeclareLocal(typeof(string)).LocalIndex;
}

Label isNotString = il.DefineLabel();
il.Emit(OpCodes.Dup); // stack is now [target][target][value][value]
il.Emit(OpCodes.Isinst, typeof(string)); // stack is now [target][target][value-as-object][string or null]
il.Emit(OpCodes.Dup);// stack is now [target][target][value-as-object][string or null][string or null]
il.Emit(OpCodes.Stloc_2); // stack is now [target][target][value-as-object][string or null]
StoreLocal(il, enumDeclareLocal); // stack is now [target][target][value-as-object][string or null]
il.Emit(OpCodes.Brfalse_S, isNotString); // stack is now [target][target][value-as-object]

il.Emit(OpCodes.Pop); // stack is now [target][target]
Expand All @@ -1694,7 +1737,7 @@ static List<FieldInfo> GetSettableFields(Type t)
il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value]

if (nullUnderlyingType != null)
{
{
il.Emit(OpCodes.Newobj, memberType.GetConstructor(new[] { nullUnderlyingType })); // stack is now [target][target][enum-value]
}
}
Expand All @@ -1707,31 +1750,50 @@ static List<FieldInfo> GetSettableFields(Type t)
{
il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value]
}
}
if (specializedConstructor == null)
{
// Store the value in the property/field
if (item.Property != null)
{
if (type.IsValueType)
{
il.Emit(OpCodes.Call, item.Property.Setter); // stack is now [target]
}
else
{
il.Emit(OpCodes.Callvirt, item.Property.Setter); // stack is now [target]
}
}
else
{
il.Emit(OpCodes.Stfld, item.Field); // stack is now [target]
}
}

il.Emit(OpCodes.Br_S, finishLabel); // stack is now [target]

// Store the value in the property/field
if (item.Property != null)
il.MarkLabel(isDbNullLabel); // incoming stack: [target][target][value]
if (specializedConstructor != null)
{
if (type.IsValueType)
il.Emit(OpCodes.Pop);
if (item.Property.Type.IsValueType)
{
il.Emit(OpCodes.Call, item.Property.Setter); // stack is now [target]
int localIndex = il.DeclareLocal(item.Property.Type).LocalIndex;
LoadLocalAddress(il, localIndex);
il.Emit(OpCodes.Initobj, item.Property.Type);
LoadLocal(il, localIndex);
}
else
{
il.Emit(OpCodes.Callvirt, item.Property.Setter); // stack is now [target]
il.Emit(OpCodes.Ldnull);
}
}
else
{
il.Emit(OpCodes.Stfld, item.Field); // stack is now [target]
il.Emit(OpCodes.Pop); // stack is now [target][target]
il.Emit(OpCodes.Pop); // stack is now [target]
}

il.Emit(OpCodes.Br_S, finishLabel); // stack is now [target]

il.MarkLabel(isDbNullLabel); // incoming stack: [target][target][value]

il.Emit(OpCodes.Pop); // stack is now [target][target]
il.Emit(OpCodes.Pop); // stack is now [target]

if (first && returnNullIfFirstMissing)
{
Expand All @@ -1752,6 +1814,10 @@ static List<FieldInfo> GetSettableFields(Type t)
}
else
{
if (specializedConstructor != null)
{
il.Emit(OpCodes.Newobj, specializedConstructor);
}
il.Emit(OpCodes.Stloc_1); // stack is empty
}
il.MarkLabel(allDone);
Expand All @@ -1771,6 +1837,61 @@ static List<FieldInfo> GetSettableFields(Type t)
return (Func<IDataReader, object>)dm.CreateDelegate(typeof(Func<IDataReader,object>));
}

private static void LoadLocal(ILGenerator il, int index)
{
if(index < 0 || index >= short.MaxValue) throw new ArgumentNullException("index");
switch(index)
{
case 0: il.Emit(OpCodes.Ldloc_0); break;
case 1: il.Emit(OpCodes.Ldloc_1); break;
case 2: il.Emit(OpCodes.Ldloc_2); break;
case 3: il.Emit(OpCodes.Ldloc_3); break;
default:
if (index <= 255)
{
il.Emit(OpCodes.Ldloc_S, (byte)index);
}
else
{
il.Emit(OpCodes.Ldloc, (short)index);
}
break;
}
}
private static void StoreLocal(ILGenerator il, int index)
{
if (index < 0 || index >= short.MaxValue) throw new ArgumentNullException("index");
switch (index)
{
case 0: il.Emit(OpCodes.Stloc_0); break;
case 1: il.Emit(OpCodes.Stloc_1); break;
case 2: il.Emit(OpCodes.Stloc_2); break;
case 3: il.Emit(OpCodes.Stloc_3); break;
default:
if (index <= 255)
{
il.Emit(OpCodes.Stloc_S, (byte)index);
}
else
{
il.Emit(OpCodes.Stloc, (short)index);
}
break;
}
}
private static void LoadLocalAddress(ILGenerator il, int index)
{
if (index < 0 || index >= short.MaxValue) throw new ArgumentNullException("index");

if (index <= 255)
{
il.Emit(OpCodes.Ldloca_S, (byte)index);
}
else
{
il.Emit(OpCodes.Ldloca, (short)index);
}
}
/// <summary>
/// Throws a data exception, only used internally
/// </summary>
Expand Down

0 comments on commit 63d3747

Please sign in to comment.