Commit f1ad61c7 authored by Joseph Musser's avatar Joseph Musser Committed by Marc Gravell

Use LocalBuilders rather than hardcoding or passing local indexes (#1282)

* Pass LocalBuilders instead of indexes since ILGenerator already optimizes the opcode used

* Eradicate magic local indexes to make refactoring safer
parent 6754fe27
......@@ -2391,19 +2391,21 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql)
var il = dm.GetILGenerator();
bool isStruct = type.IsValueType();
bool haveInt32Arg1 = false;
var sizeLocal = (LocalBuilder)null;
il.Emit(OpCodes.Ldarg_1); // stack is now [untyped-param]
LocalBuilder typedParameterLocal;
if (isStruct)
{
il.DeclareLocal(type.MakeByRefType()); // note: ref-local
typedParameterLocal = il.DeclareLocal(type.MakeByRefType()); // note: ref-local
il.Emit(OpCodes.Unbox, type); // stack is now [typed-param]
}
else
{
il.DeclareLocal(type); // 0
typedParameterLocal = il.DeclareLocal(type);
il.Emit(OpCodes.Castclass, type); // stack is now [typed-param]
}
il.Emit(OpCodes.Stloc_0);// stack is now empty
il.Emit(OpCodes.Stloc, typedParameterLocal); // stack is now empty
il.Emit(OpCodes.Ldarg_0); // stack is now [command]
il.EmitCall(OpCodes.Callvirt, typeof(IDbCommand).GetProperty(nameof(IDbCommand.Parameters)).GetGetMethod(), null); // stack is now [parameters]
......@@ -2482,7 +2484,7 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql)
{
if (typeof(ICustomQueryParameter).IsAssignableFrom(prop.PropertyType))
{
il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [typed-param]
il.Emit(OpCodes.Ldloc, typedParameterLocal); // stack is now [parameters] [typed-param]
il.Emit(callOpCode, prop.GetGetMethod()); // stack is [parameters] [custom]
il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [custom] [command]
il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [custom] [command] [name]
......@@ -2497,7 +2499,7 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql)
// this actually represents special handling for list types;
il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [command]
il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [command] [name]
il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [command] [name] [typed-param]
il.Emit(OpCodes.Ldloc, typedParameterLocal); // stack is now [parameters] [command] [name] [typed-param]
il.Emit(callOpCode, prop.GetGetMethod()); // stack is [parameters] [command] [name] [typed-value]
if (prop.PropertyType.IsValueType())
{
......@@ -2531,7 +2533,7 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql)
if (dbType == DbType.Object && prop.PropertyType == typeof(object)) // includes dynamic
{
// look it up from the param value
il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [[parameters]] [parameter] [parameter] [typed-param]
il.Emit(OpCodes.Ldloc, typedParameterLocal); // stack is now [parameters] [[parameters]] [parameter] [parameter] [typed-param]
il.Emit(callOpCode, prop.GetGetMethod()); // stack is [parameters] [[parameters]] [parameter] [parameter] [object-value]
il.Emit(OpCodes.Call, typeof(SqlMapper).GetMethod(nameof(SqlMapper.GetDbType), BindingFlags.Static | BindingFlags.Public)); // stack is now [parameters] [[parameters]] [parameter] [parameter] [db-type]
}
......@@ -2548,7 +2550,7 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql)
il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty(nameof(IDataParameter.Direction)).GetSetMethod(), null);// stack is now [parameters] [[parameters]] [parameter]
il.Emit(OpCodes.Dup);// stack is now [parameters] [[parameters]] [parameter] [parameter]
il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [[parameters]] [parameter] [parameter] [typed-param]
il.Emit(OpCodes.Ldloc, typedParameterLocal); // stack is now [parameters] [[parameters]] [parameter] [parameter] [typed-param]
il.Emit(callOpCode, prop.GetGetMethod()); // stack is [parameters] [[parameters]] [parameter] [parameter] [typed-value]
bool checkForNull;
if (prop.PropertyType.IsValueType())
......@@ -2600,10 +2602,9 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql)
}
if (checkForNull)
{
if ((dbType == DbType.String || dbType == DbType.AnsiString) && !haveInt32Arg1)
if ((dbType == DbType.String || dbType == DbType.AnsiString) && sizeLocal == null)
{
il.DeclareLocal(typeof(int));
haveInt32Arg1 = true;
sizeLocal = il.DeclareLocal(typeof(int));
}
// relative stack: [boxed value]
il.Emit(OpCodes.Dup);// relative stack: [boxed value] [boxed value]
......@@ -2616,7 +2617,7 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql)
if (dbType == DbType.String || dbType == DbType.AnsiString)
{
EmitInt32(il, 0);
il.Emit(OpCodes.Stloc_1);
il.Emit(OpCodes.Stloc, sizeLocal);
}
if (allDone != null) il.Emit(OpCodes.Br_S, allDone.Value);
il.MarkLabel(notNull);
......@@ -2633,7 +2634,7 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql)
il.MarkLabel(isLong);
EmitInt32(il, -1); // [string] [-1]
il.MarkLabel(lenDone);
il.Emit(OpCodes.Stloc_1); // [string]
il.Emit(OpCodes.Stloc, sizeLocal); // [string]
}
if (prop.PropertyType.FullName == LinqBinary)
{
......@@ -2658,11 +2659,11 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql)
{
var endOfSize = il.DefineLabel();
// don't set if 0
il.Emit(OpCodes.Ldloc_1); // [parameters] [[parameters]] [parameter] [size]
il.Emit(OpCodes.Ldloc, sizeLocal); // [parameters] [[parameters]] [parameter] [size]
il.Emit(OpCodes.Brfalse_S, endOfSize); // [parameters] [[parameters]] [parameter]
il.Emit(OpCodes.Dup);// stack is now [parameters] [[parameters]] [parameter] [parameter]
il.Emit(OpCodes.Ldloc_1); // stack is now [parameters] [[parameters]] [parameter] [parameter] [size]
il.Emit(OpCodes.Ldloc, sizeLocal); // stack is now [parameters] [[parameters]] [parameter] [parameter] [size]
il.EmitCall(OpCodes.Callvirt, typeof(IDbDataParameter).GetProperty(nameof(IDbDataParameter.Size)).GetSetMethod(), null); // stack is now [parameters] [[parameters]] [parameter]
il.MarkLabel(endOfSize);
......@@ -2715,7 +2716,7 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql)
if (prop != null)
{
il.Emit(OpCodes.Ldstr, literal.Token);
il.Emit(OpCodes.Ldloc_0); // command, sql, typed parameter
il.Emit(OpCodes.Ldloc, typedParameterLocal); // command, sql, typed parameter
il.EmitCall(callOpCode, prop.GetGetMethod(), null); // command, sql, typed value
Type propType = prop.PropertyType;
var typeCode = TypeExtensions.GetTypeCode(propType);
......@@ -3046,9 +3047,9 @@ private static LocalBuilder GetTempLocal(ILGenerator il, ref Dictionary<Type, Lo
}
if (initAndLoad)
{
il.Emit(OpCodes.Ldloca, (short)found.LocalIndex);
il.Emit(OpCodes.Ldloca, found);
il.Emit(OpCodes.Initobj, type);
il.Emit(OpCodes.Ldloca, (short)found.LocalIndex);
il.Emit(OpCodes.Ldloca, found);
il.Emit(OpCodes.Ldobj, type);
}
return found;
......@@ -3135,7 +3136,7 @@ private static void GenerateValueTupleDeserializer(Type valueTupleType, IDataRea
}
}
var enumDeclareLocal = -1;
var stringEnumLocal = (LocalBuilder)null;
for (var i = 0; i < languageTupleElementTypes.Count; i++)
{
......@@ -3146,7 +3147,7 @@ private static void GenerateValueTupleDeserializer(Type valueTupleType, IDataRea
LoadReaderValueOrBranchToDBNullLabel(
il,
startBound + i,
ref enumDeclareLocal,
ref stringEnumLocal,
valueCopyLocal: null,
reader.GetFieldType(startBound + i),
targetType,
......@@ -3179,9 +3180,9 @@ private static void GenerateValueTupleDeserializer(Type valueTupleType, IDataRea
private static void GenerateDeserializerFromMap(Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing, ILGenerator il)
{
var currentIndexDiagnosticLocal = il.DeclareLocal(typeof(int));
il.DeclareLocal(type);
var returnValueLocal = il.DeclareLocal(type);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Stloc, currentIndexDiagnosticLocal);
var names = Enumerable.Range(startBound, length).Select(i => reader.GetName(i)).ToArray();
......@@ -3196,7 +3197,7 @@ private static void GenerateDeserializerFromMap(Type type, IDataReader reader, i
Dictionary<Type, LocalBuilder> structLocals = null;
if (type.IsValueType())
{
il.Emit(OpCodes.Ldloca_S, (byte)1);
il.Emit(OpCodes.Ldloca, returnValueLocal);
il.Emit(OpCodes.Initobj, type);
}
else
......@@ -3224,12 +3225,12 @@ private static void GenerateDeserializerFromMap(Type type, IDataReader reader, i
}
il.Emit(OpCodes.Newobj, explicitConstr);
il.Emit(OpCodes.Stloc_1);
il.Emit(OpCodes.Stloc, returnValueLocal);
#if !NETSTANDARD1_3
supportInitialize = typeof(ISupportInitialize).IsAssignableFrom(type);
if (supportInitialize)
{
il.Emit(OpCodes.Ldloc_1);
il.Emit(OpCodes.Ldloc, returnValueLocal);
il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod(nameof(ISupportInitialize.BeginInit)), null);
}
#endif
......@@ -3246,12 +3247,12 @@ private static void GenerateDeserializerFromMap(Type type, IDataReader reader, i
if (ctor.GetParameters().Length == 0)
{
il.Emit(OpCodes.Newobj, ctor);
il.Emit(OpCodes.Stloc_1);
il.Emit(OpCodes.Stloc, returnValueLocal);
#if !NETSTANDARD1_3
supportInitialize = typeof(ISupportInitialize).IsAssignableFrom(type);
if (supportInitialize)
{
il.Emit(OpCodes.Ldloc_1);
il.Emit(OpCodes.Ldloc, returnValueLocal);
il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod(nameof(ISupportInitialize.BeginInit)), null);
}
#endif
......@@ -3266,11 +3267,11 @@ private static void GenerateDeserializerFromMap(Type type, IDataReader reader, i
il.BeginExceptionBlock();
if (type.IsValueType())
{
il.Emit(OpCodes.Ldloca_S, (byte)1);// [target]
il.Emit(OpCodes.Ldloca, returnValueLocal); // [target]
}
else if (specializedConstructor == null)
{
il.Emit(OpCodes.Ldloc_1);// [target]
il.Emit(OpCodes.Ldloc, returnValueLocal); // [target]
}
var members = (specializedConstructor != null
......@@ -3281,7 +3282,8 @@ private static void GenerateDeserializerFromMap(Type type, IDataReader reader, i
bool first = true;
var allDone = il.DefineLabel();
int enumDeclareLocal = -1, valueCopyLocal = il.DeclareLocal(typeof(object)).LocalIndex;
var stringEnumLocal = (LocalBuilder)null;
var valueCopyDiagnosticLocal = il.DeclareLocal(typeof(object));
bool applyNullSetting = Settings.ApplyNullValues;
foreach (var item in members)
{
......@@ -3296,7 +3298,7 @@ private static void GenerateDeserializerFromMap(Type type, IDataReader reader, i
EmitInt32(il, index);
il.Emit(OpCodes.Stloc, currentIndexDiagnosticLocal);
LoadReaderValueOrBranchToDBNullLabel(il, index, ref enumDeclareLocal, valueCopyLocal, reader.GetFieldType(index), memberType, out var isDbNullLabel);
LoadReaderValueOrBranchToDBNullLabel(il, index, ref stringEnumLocal, valueCopyDiagnosticLocal, reader.GetFieldType(index), memberType, out var isDbNullLabel);
if (specializedConstructor == null)
{
......@@ -3353,7 +3355,7 @@ private static void GenerateDeserializerFromMap(Type type, IDataReader reader, i
{
il.Emit(OpCodes.Pop);
il.Emit(OpCodes.Ldnull); // stack is now [null]
il.Emit(OpCodes.Stloc_1);
il.Emit(OpCodes.Stloc, returnValueLocal);
il.Emit(OpCodes.Br, allDone);
}
......@@ -3372,11 +3374,11 @@ private static void GenerateDeserializerFromMap(Type type, IDataReader reader, i
{
il.Emit(OpCodes.Newobj, specializedConstructor);
}
il.Emit(OpCodes.Stloc_1); // stack is empty
il.Emit(OpCodes.Stloc, returnValueLocal); // stack is empty
#if !NETSTANDARD1_3
if (supportInitialize)
{
il.Emit(OpCodes.Ldloc_1);
il.Emit(OpCodes.Ldloc, returnValueLocal);
il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod(nameof(ISupportInitialize.EndInit)), null);
}
#endif
......@@ -3385,11 +3387,11 @@ private static void GenerateDeserializerFromMap(Type type, IDataReader reader, i
il.BeginCatchBlock(typeof(Exception)); // stack is Exception
il.Emit(OpCodes.Ldloc, currentIndexDiagnosticLocal); // stack is Exception, index
il.Emit(OpCodes.Ldarg_0); // stack is Exception, index, reader
LoadLocal(il, valueCopyLocal); // stack is Exception, index, reader, value
il.Emit(OpCodes.Ldloc, valueCopyDiagnosticLocal); // stack is Exception, index, reader, value
il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod(nameof(SqlMapper.ThrowDataException)), null);
il.EndExceptionBlock();
il.Emit(OpCodes.Ldloc_1); // stack is [rval]
il.Emit(OpCodes.Ldloc, returnValueLocal); // stack is [rval]
if (type.IsValueType())
{
il.Emit(OpCodes.Box, type);
......@@ -3401,10 +3403,10 @@ private static void LoadDefaultValue(ILGenerator il, Type type)
{
if (type.IsValueType())
{
int localIndex = il.DeclareLocal(type).LocalIndex;
LoadLocalAddress(il, localIndex);
var local = il.DeclareLocal(type);
il.Emit(OpCodes.Ldloca, local);
il.Emit(OpCodes.Initobj, type);
LoadLocal(il, localIndex);
il.Emit(OpCodes.Ldloc, local);
}
else
{
......@@ -3412,7 +3414,7 @@ private static void LoadDefaultValue(ILGenerator il, Type type)
}
}
private static void LoadReaderValueOrBranchToDBNullLabel(ILGenerator il, int index, ref int enumDeclareLocal, int? valueCopyLocal, Type colType, Type memberType, out Label isDbNullLabel)
private static void LoadReaderValueOrBranchToDBNullLabel(ILGenerator il, int index, ref LocalBuilder stringEnumLocal, LocalBuilder valueCopyLocal, Type colType, Type memberType, out Label isDbNullLabel)
{
isDbNullLabel = il.DefineLabel();
il.Emit(OpCodes.Ldarg_0); // stack is now [...][reader]
......@@ -3422,7 +3424,7 @@ private static void LoadReaderValueOrBranchToDBNullLabel(ILGenerator il, int ind
if (valueCopyLocal != null)
{
il.Emit(OpCodes.Dup); // stack is now [...][value-as-object][value-as-object]
StoreLocal(il, valueCopyLocal.Value); // stack is now [...][value-as-object]
il.Emit(OpCodes.Stloc, valueCopyLocal); // stack is now [...][value-as-object]
}
if (memberType == typeof(char) || memberType == typeof(char?))
......@@ -3446,15 +3448,15 @@ private static void LoadReaderValueOrBranchToDBNullLabel(ILGenerator il, int ind
Type numericType = Enum.GetUnderlyingType(unboxType);
if (colType == typeof(string))
{
if (enumDeclareLocal == -1)
if (stringEnumLocal == null)
{
enumDeclareLocal = il.DeclareLocal(typeof(string)).LocalIndex;
stringEnumLocal = il.DeclareLocal(typeof(string));
}
il.Emit(OpCodes.Castclass, typeof(string)); // stack is now [...][string]
StoreLocal(il, enumDeclareLocal); // stack is now [...]
il.Emit(OpCodes.Stloc, stringEnumLocal); // stack is now [...]
il.Emit(OpCodes.Ldtoken, unboxType); // stack is now [...][enum-type-token]
il.EmitCall(OpCodes.Call, typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle)), null);// stack is now [...][enum-type]
LoadLocal(il, enumDeclareLocal); // stack is now [...][enum-type][string]
il.Emit(OpCodes.Ldloc, stringEnumLocal); // stack is now [...][enum-type][string]
il.Emit(OpCodes.Ldc_I4_1); // stack is now [...][enum-type][string][true]
il.EmitCall(OpCodes.Call, enumParse, null); // stack is now [...][enum-as-object]
il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [...][typed-value]
......@@ -3608,64 +3610,6 @@ private static MethodInfo ResolveOperator(MethodInfo[] methods, Type from, Type
return null;
}
private static void LoadLocal(ILGenerator il, int index)
{
if (index < 0 || index >= short.MaxValue) throw new ArgumentNullException(nameof(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(nameof(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(nameof(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>
......@@ -3795,7 +3739,7 @@ public static void SetTypeName(this DataTable table, string typeName)
public static ICustomQueryParameter AsTableValuedParameter<T>(this IEnumerable<T> list, string typeName = null) where T : IDataRecord =>
new SqlDataRecordListTVPParameter<T>(list, typeName);
/*
/*
/// <summary>
/// Used to pass a IEnumerable&lt;SqlDataRecord&gt; as a TableValuedParameter.
/// </summary>
......
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