public static void GenerateDeserializerFromMap(Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing, ILGenerator il) { var currentIndexDiagnosticLocal = il.DeclareLocal(typeof(int)); var returnValueLocal = il.DeclareLocal(type); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Stloc, currentIndexDiagnosticLocal); var names = Enumerable.Range(startBound, length).Select(i => reader.GetName(i)).ToArray(); ITypeMap typeMap = TypeMapHelper.GetTypeMap(type); int index = startBound; ConstructorInfo specializedConstructor = null; bool supportInitialize = false; Dictionary <Type, LocalBuilder> structLocals = null; if (type.IsValueType) { il.Emit(OpCodes.Ldloca, returnValueLocal); il.Emit(OpCodes.Initobj, type); } else { var types = new Type[length]; for (int i = startBound; i < startBound + length; i++) { types[i - startBound] = reader.GetFieldType(i); } var explicitConstr = typeMap.FindExplicitConstructor(); if (explicitConstr != null) { var consPs = explicitConstr.GetParameters(); foreach (var p in consPs) { if (!p.ParameterType.IsValueType) { il.Emit(OpCodes.Ldnull); } else { ILHelper.GetTempLocal(il, ref structLocals, p.ParameterType, true); } } il.Emit(OpCodes.Newobj, explicitConstr); il.Emit(OpCodes.Stloc, returnValueLocal); supportInitialize = typeof(ISupportInitialize).IsAssignableFrom(type); if (supportInitialize) { il.Emit(OpCodes.Ldloc, returnValueLocal); il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod(nameof(ISupportInitialize.BeginInit)), null); } } else { var ctor = typeMap.FindConstructor(names, types); if (ctor == null) { string proposedTypes = "(" + string.Join(", ", types.Select((t, i) => t.FullName + " " + names[i]).ToArray()) + ")"; throw new InvalidOperationException($"A parameterless default constructor or one matching signature {proposedTypes} is required for {type.FullName} materialization"); } if (ctor.GetParameters().Length == 0) { il.Emit(OpCodes.Newobj, ctor); il.Emit(OpCodes.Stloc, returnValueLocal); supportInitialize = typeof(ISupportInitialize).IsAssignableFrom(type); if (supportInitialize) { il.Emit(OpCodes.Ldloc, returnValueLocal); il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod(nameof(ISupportInitialize.BeginInit)), null); } } else { specializedConstructor = ctor; } } } il.BeginExceptionBlock(); if (type.IsValueType) { il.Emit(OpCodes.Ldloca, returnValueLocal); // [target] } else if (specializedConstructor == null) { il.Emit(OpCodes.Ldloc, returnValueLocal); // [target] } var members = (specializedConstructor != null ? names.Select(n => typeMap.GetConstructorParameter(specializedConstructor, n)) : names.Select(n => typeMap.GetMember(n))).ToList(); // stack is now [target] bool first = true; var allDone = il.DefineLabel(); var stringEnumLocal = (LocalBuilder)null; var valueCopyDiagnosticLocal = il.DeclareLocal(typeof(object)); bool applyNullSetting = false;//Settings.ApplyNullValues; foreach (var item in members) { if (item != null) { if (specializedConstructor == null) { il.Emit(OpCodes.Dup); // stack is now [target][target] } Label finishLabel = il.DefineLabel(); Type memberType = item.MemberType; // Save off the current index for access if an exception is thrown ILHelper.EmitInt32(il, index); il.Emit(OpCodes.Stloc, currentIndexDiagnosticLocal); ILHelper.LoadReaderValueOrBranchToDBNullLabel(il, index, ref stringEnumLocal, valueCopyDiagnosticLocal, reader.GetFieldType(index), memberType, out var isDbNullLabel); if (specializedConstructor == null) { // Store the value in the property/field if (item.Property != null) { il.Emit(type.IsValueType ? OpCodes.Call : OpCodes.Callvirt, DefaultTypeMap.GetPropertySetter(item.Property, type)); } else { il.Emit(OpCodes.Stfld, item.Field); // stack is now [target] } } il.Emit(OpCodes.Br_S, finishLabel); // stack is now [target] il.MarkLabel(isDbNullLabel); // incoming stack: [target][target][value] if (specializedConstructor != null) { il.Emit(OpCodes.Pop); ILHelper.LoadDefaultValue(il, item.MemberType); } else if (applyNullSetting && (!memberType.IsValueType || Nullable.GetUnderlyingType(memberType) != null)) { il.Emit(OpCodes.Pop); // stack is now [target][target] // can load a null with this value if (memberType.IsValueType) { // must be Nullable<T> for some T ILHelper.GetTempLocal(il, ref structLocals, memberType, true); // stack is now [target][target][null] } else { // regular reference-type il.Emit(OpCodes.Ldnull); // stack is now [target][target][null] } // Store the value in the property/field if (item.Property != null) { il.Emit(type.IsValueType ? OpCodes.Call : OpCodes.Callvirt, DefaultTypeMap.GetPropertySetter(item.Property, type)); // stack is now [target] } else { il.Emit(OpCodes.Stfld, item.Field); // stack is now [target] } } else { il.Emit(OpCodes.Pop); // stack is now [target][target] il.Emit(OpCodes.Pop); // stack is now [target] } if (first && returnNullIfFirstMissing) { il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ldnull); // stack is now [null] il.Emit(OpCodes.Stloc, returnValueLocal); il.Emit(OpCodes.Br, allDone); } il.MarkLabel(finishLabel); } first = false; index++; } if (type.IsValueType) { il.Emit(OpCodes.Pop); } else { if (specializedConstructor != null) { il.Emit(OpCodes.Newobj, specializedConstructor); } il.Emit(OpCodes.Stloc, returnValueLocal); // stack is empty if (supportInitialize) { il.Emit(OpCodes.Ldloc, returnValueLocal); il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod(nameof(ISupportInitialize.EndInit)), null); } } il.MarkLabel(allDone); 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 il.Emit(OpCodes.Ldloc, valueCopyDiagnosticLocal); // stack is Exception, index, reader, value il.EmitCall(OpCodes.Call, typeof(DataReaderTypeMapper).GetMethod(nameof(DataReaderTypeMapper.ThrowDataException)), null); il.EndExceptionBlock(); il.Emit(OpCodes.Ldloc, returnValueLocal); // stack is [rval] if (type.IsValueType) { il.Emit(OpCodes.Box, type); } il.Emit(OpCodes.Ret); }
public static void GenerateValueTupleDeserializer(Type valueTupleType, IDataReader reader, int startBound, int length, ILGenerator il) { var nullableUnderlyingType = Nullable.GetUnderlyingType(valueTupleType); var currentValueTupleType = nullableUnderlyingType ?? valueTupleType; var constructors = new List <ConstructorInfo>(); var languageTupleElementTypes = new List <Type>(); while (true) { var arity = int.Parse(currentValueTupleType.Name.Substring("ValueTuple`".Length), GlobalSettings.Culture); var constructorParameterTypes = new Type[arity]; var restField = (FieldInfo)null; foreach (var field in currentValueTupleType.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) { if (field.Name == "Rest") { restField = field; } else if (field.Name.StartsWith("Item", StringComparison.Ordinal)) { var elementNumber = int.Parse(field.Name.Substring("Item".Length), GlobalSettings.Culture); constructorParameterTypes[elementNumber - 1] = field.FieldType; } } var itemFieldCount = constructorParameterTypes.Length; if (restField != null) { itemFieldCount--; } for (var i = 0; i < itemFieldCount; i++) { languageTupleElementTypes.Add(constructorParameterTypes[i]); } if (restField != null) { constructorParameterTypes[constructorParameterTypes.Length - 1] = restField.FieldType; } constructors.Add(currentValueTupleType.GetConstructor(constructorParameterTypes)); if (restField is null) { break; } currentValueTupleType = restField.FieldType; if (!IsValueTuple(currentValueTupleType)) { throw new InvalidOperationException("The Rest field of a ValueTuple must contain a nested ValueTuple of arity 1 or greater."); } } var stringEnumLocal = (LocalBuilder)null; for (var i = 0; i < languageTupleElementTypes.Count; i++) { var targetType = languageTupleElementTypes[i]; if (i < length) { ILHelper.LoadReaderValueOrBranchToDBNullLabel( il, startBound + i, ref stringEnumLocal, valueCopyLocal: null, reader.GetFieldType(startBound + i), targetType, out var isDbNullLabel); var finishLabel = il.DefineLabel(); il.Emit(OpCodes.Br_S, finishLabel); il.MarkLabel(isDbNullLabel); il.Emit(OpCodes.Pop); ILHelper.LoadDefaultValue(il, targetType); il.MarkLabel(finishLabel); } else { ILHelper.LoadDefaultValue(il, targetType); } } for (var i = constructors.Count - 1; i >= 0; i--) { il.Emit(OpCodes.Newobj, constructors[i]); } if (nullableUnderlyingType != null) { var nullableTupleConstructor = valueTupleType.GetConstructor(new[] { nullableUnderlyingType }); il.Emit(OpCodes.Newobj, nullableTupleConstructor); } il.Emit(OpCodes.Box, valueTupleType); il.Emit(OpCodes.Ret); }