private ConstructedValueExpression CompileArrayOfScalar(MapTypeContext context, ConstructorInfo constructor, List <ColumnInfo> columns, Type elementType) { var arrayVar = context.CreateVariable(context.TargetType, "array"); var expressions = new List <Expression>(); var variables = new List <ParameterExpression>(); variables.Add(arrayVar); expressions.Add( Expression.Assign( arrayVar, Expression.New( constructor, Expression.Constant(columns.Count) ) ) ); for (int i = 0; i < columns.Count; i++) { var columnState = context.GetSubstateForColumn(columns[i], elementType, null); var getScalarExpression = _scalars.Compile(columnState); expressions.AddRange(getScalarExpression.Expressions); expressions.Add( Expression.Assign( Expression.ArrayAccess( arrayVar, Expression.Constant(i) ), getScalarExpression.FinalValue ) ); variables.AddRange(getScalarExpression.Variables); } return(new ConstructedValueExpression(expressions, arrayVar, variables)); }
public ConstructedValueExpression Compile(MapTypeContext context) { if (!context.TargetType.IsSupportedPrimitiveType() && context.TargetType != typeof(object)) { return(ConstructedValueExpression.Nothing); } var column = context.SingleColumn(); if (column == null) { return(ConstructedValueExpression.Nothing); } // Pull the value out of the reader into the rawVar var rawVar = context.CreateVariable(typeof(object), "raw"); var getRawStmt = Expression.Assign( rawVar, Expression.Call( context.RecordParameter, _getValueMethod, Expression.Constant(column.Index) ) ); var rawConvertExpr = MapScalar(context.TargetType, column, rawVar); column.MarkMapped(); return(new ConstructedValueExpression(new[] { getRawStmt }, rawConvertExpr, new[] { rawVar })); }
private static ConstructedValueExpression AddFactoryMethodCallExpression(MapTypeContext context, Func <object> factory, ParameterExpression instanceVar) { var expressions = new Expression[] { // Call the factory method and assign the result to the instance variable Expression.Assign( instanceVar, Expression.Convert( Expression.Call( Expression.Constant(factory.Target), factory.Method ), context.TargetType ) ), // check the instance variable value here to make sure it's not null. If it is null // we will throw an exception which is (slightly) more helpful than the default // NullReferenceException Expression.IfThen( Expression.Equal( instanceVar, Expression.Constant(null) ), Expression.Throw( Expression.Call( _createMapExceptionMethod, Expression.Constant(context.TargetType) ) ) ) }; return(new ConstructedValueExpression(expressions, instanceVar, null)); }
public ConstructedValueExpression Compile(MapTypeContext context) { if (!IsSupportedType(context.TargetType)) { return(ConstructedValueExpression.Nothing); } // Get the concrete type to construct, the element type to populate, and the Add // method to call for each value var(collectionType, elementType, constructor, addMethod) = GetConcreteTypeInfo(context); Debug.Assert(collectionType != null); Debug.Assert(context.TargetType.IsAssignableFrom(collectionType)); Debug.Assert(elementType != null); Debug.Assert(addMethod != null); Debug.Assert(constructor != null); var originalTargetType = context.TargetType; context = context.ChangeTargetType(collectionType); // Get a list of expressions to create the collection instance and populate it with // values var collectionVar = CollectionExpressionFactory.GetMaybeInstantiateCollectionExpression(context, constructor); var addStmts = CollectionExpressionFactory.GetCollectionPopulateStatements(_scalars, _nonScalars, context, elementType, collectionVar.FinalValue, addMethod); return(new ConstructedValueExpression( collectionVar.Expressions.Concat(addStmts.Expressions), Expression.Convert(collectionVar.FinalValue, originalTargetType), collectionVar.Variables.Concat(addStmts.Variables) )); }
private static Type GetIDictionaryType(MapTypeContext context) { return(context.TargetType.GetInterfaces() .Where(i => i.IsGenericType) .FirstOrDefault(i => i.GetGenericTypeDefinition() == typeof(IDictionary <,>) && i.GenericTypeArguments[0] == typeof(string)) ?? throw MapCompilerException.InvalidDictionaryTargetType(context.TargetType)); }
public ConstructedValueExpression Compile(MapTypeContext context) { if (!IsSupportedType(context.TargetType)) { return(ConstructedValueExpression.Nothing); } // Element type and constructor should both always be available for Array types, and // we know this is an array type in MapCompiler. Just assume we have these things, but // Debug.Assert them just to be sure. var elementType = context.TargetType.GetElementType(); Debug.Assert(elementType != null); var constructor = context.TargetType.GetConstructor(new[] { typeof(int) }); Debug.Assert(constructor != null); if (elementType.IsMappableCustomObjectType()) { return(CompileArrayOfCustomObject(context, elementType, constructor)); } // In an array, object is always treated as a scalar type if (elementType.IsSupportedPrimitiveType() || elementType == typeof(object)) { var columns = context.GetColumns().ToList(); return(CompileArrayOfScalar(context, constructor, columns, elementType)); } // It's not a type we know how to support, so do nothing return(ConstructedValueExpression.Nothing); }
private ConstructedValueExpression MapTupleParameters(MapTypeContext context, MethodInfo factoryMethod, Type[] typeParams) { var expressions = new List <Expression>(); var variables = new List <ParameterExpression>(); var args = new Expression[typeParams.Length]; // We need to assign a value to every parameter. Loop over each parameter and map values where // available. When we run out of columns or have a column we cannot map, fill in a default value. // Where a value type may consume many columns (a custom object type, for example) it will // greedily consume as much as possible before moving to the next parameter. for (var i = 0; i < typeParams.Length; i++) { if (!context.HasColumns()) { args[i] = typeParams[i].GetDefaultValueExpression(); continue; } var elementState = context.ChangeTargetType(typeParams[i]); var expr = _values.Compile(elementState); if (expr.IsNothing) { args[i] = typeParams[i].GetDefaultValueExpression(); continue; } expressions.AddRange(expr.Expressions); variables.AddRange(expr.Variables); args[i] = expr.FinalValue; } return(new ConstructedValueExpression(expressions, Expression.Call(null, factoryMethod, args), variables)); }
private ConcreteCollectionInfo GetTypeInfo(MapTypeContext context) { var targetType = context.TargetType; var typeSettings = context.Settings.GetTypeSettings(targetType); if (typeSettings.BaseType != typeof(object)) { targetType = typeSettings.GetDefault().Type; } // We absolutely need a constructor here, there's no way around it. The user has asked // for a specific concrete type, we can either return that type or the configured // subclass, but we can't default to something else with a parameterless constructor var constructor = targetType.GetConstructor(Type.EmptyTypes); if (constructor == null) { throw MapCompilerException.InvalidCollectionType(targetType); } // Same with .Add(). The type absolutely must have a .Add(T) method variant or we // cannot proceed var addMethods = targetType .GetMethods(BindingFlags.Public | BindingFlags.Instance) .Where(m => m.Name == AddMethodName && m.GetParameters().Length == 1) .ToList(); if (addMethods.Count == 0) { throw MapCompilerException.InvalidCollectionType(targetType); } // Find all IEnumerable<T> interfaces. For each one we're going to look for a matching // .Add(T) method. If we find a match, that's what we're going with. var iEnumerableOfTTypes = targetType .GetInterfaces() .Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable <>)) .ToDictionary(t => t.GetGenericArguments()[0]); if (iEnumerableOfTTypes.Count > 0) { var bestAddMethod = addMethods.FirstOrDefault(m => iEnumerableOfTTypes.ContainsKey(m.GetParameters()[0].ParameterType)); if (bestAddMethod != null) { var bestAddElementType = bestAddMethod.GetParameters()[0].ParameterType; return(new ConcreteCollectionInfo(targetType, bestAddElementType, constructor, bestAddMethod)); } } // At this point we know the collection must be IEnumerable because of precondition // checks. So we'll just use any available Add method var addMethod = addMethods.FirstOrDefault(); var elementType = addMethod.GetParameters()[0].ParameterType; return(new ConcreteCollectionInfo(targetType, elementType, constructor, addMethod)); }
public ConstructedValueExpression Compile(MapTypeContext context) { for (int i = 0; i < _compilers.Count; i++) { var result = _compilers[i].Compile(context); if (!result.IsNothing) { return(result); } } return(ConstructedValueExpression.Nothing); }
private ConstructedValueExpression AddInstantiationExpressionForSpecificType(MapTypeContext context, ISpecificTypeSettings specificType, ParameterExpression instanceVar) { // If we have a factory method, invoke that to get the instance, otherwise fall back // to finding and calling a suitable constructor var factory = specificType.GetFactory(); if (factory != null) { return(AddFactoryMethodCallExpression(context, factory, instanceVar)); } return(GetConstructorCallExpression(context, specificType, instanceVar)); }
public ConstructedValueExpression Compile(MapTypeContext context) { if (!IsSupportedType(context.TargetType)) { return(ConstructedValueExpression.Nothing); } var typeParams = context.TargetType.GenericTypeArguments; var factoryMethod = GetTupleFactoryMethod(typeParams); return(MapTupleParameters(context, factoryMethod, typeParams)); }
private ConcreteCollectionInfo GetConcreteTypeInfo(MapTypeContext context) { // By the time we get into this Compiler we have already limited the valid TargetType // values which are possible. At this point we either have a non-generic collection // interface (IEnumerable, ICollection, IList) or we have a generic collection // interface type (IEnumerable<T>, ICollection<T>, IReadOnlyCollection<T>, IList<T> // or IReadOnlyList<T>). Separate out because we have different strategies for each if (context.TargetType.IsGenericType && context.TargetType.GenericTypeArguments.Length > 0) { return(GetConcreteTypeInfoForGenericInterfaceType(context)); } return(GetConcreteTypeInfoForInterfaceType(context)); }
public ConstructedValueExpression Compile(MapTypeContext context) { if (context.TargetType != typeof(object)) { return(ConstructedValueExpression.Nothing); } // At the top-level (name==null) we convert to dictionary to preserve column name information if (context.Name == null) { // Dictionary mapping logic will group by key name and recurse. When we get back to the // ObjectCompiler, we will have a smaller set of columns and we will have a non-null name var dictState = context.ChangeTargetType(typeof(Dictionary <string, object>)); var dictExpr = _dictionaries.Compile(dictState); return(new ConstructedValueExpression(dictExpr.Expressions, Expression.Convert(dictExpr.FinalValue, typeof(object)), dictExpr.Variables)); } var columns = context.GetColumns().ToList(); var numColumns = columns.Count; // If we have no columns, just return null if (numColumns == 0) { return(new ConstructedValueExpression( Expression.Convert( Expression.Constant(null), typeof(object) ) )); } // If we have exactly one column, map the value as a scalar and return a single result if (numColumns == 1) { var firstColumn = columns[0]; var objectState = context.GetSubstateForColumn(firstColumn, typeof(object), null); var asScalar = _scalars.Compile(objectState); return(new ConstructedValueExpression(asScalar.Expressions, Expression.Convert(asScalar.FinalValue, typeof(object)), asScalar.Variables)); } // If we have more than one column, map to object[] var arrayState = context.ChangeTargetType(typeof(object[])); var exprs = _arrays.Compile(arrayState); return(new ConstructedValueExpression(exprs.Expressions, Expression.Convert(exprs.FinalValue, typeof(object)), exprs.Variables)); }
private ConcreteTypeInfo GetConcreteTypeInfo(MapTypeContext context, Type keyType, Type elementType) { // If we have a custom type try to use that. If we don't have one, or it fails any // of the precondition checks, fall back to Dictionary<string, T> var typeSettings = context.Settings.GetTypeSettings(context.TargetType); if (typeSettings.BaseType != typeof(object)) { var customType = typeSettings.GetDefault().Type; var constructor = customType.GetConstructor(Type.EmptyTypes); var addMethod = customType.GetMethod(AddMethodName, BindingFlags.Public | BindingFlags.Instance, null, new[] { keyType, elementType }, null); if (constructor == null || addMethod == null) { return(GetDictionaryOfTTypeInfo(keyType, elementType)); } return(new ConcreteTypeInfo(customType, constructor, addMethod)); } return(GetDictionaryOfTTypeInfo(keyType, elementType)); }
private ConstructedValueExpression CompileArrayOfCustomObject(MapTypeContext context, Type elementType, ConstructorInfo constructor) { var expressions = new List <Expression>(); var variables = new List <ParameterExpression>(); // We don't loop here because we need to know how many items we have in the array and we // can't calculate that because the custom object might consume any number of columns. We don't // know until after each iteration has returned how many columns are left. We could map to // a List and then .ToArray() to get the array, but that seems wasteful. var arrayVar = context.CreateVariable(context.TargetType, "array"); variables.Add(arrayVar); var elementState = context.ChangeTargetType(elementType); var result = _customObjects.Compile(elementState); expressions.AddRange(result.Expressions); // Create a new array with a length of 1 expressions.Add( Expression.Assign( arrayVar, Expression.New( constructor, Expression.Constant(1) ) ) ); // Set the value to the first slot in the array expressions.Add( Expression.Assign( Expression.ArrayAccess( arrayVar, Expression.Constant(0) ), result.FinalValue ) ); variables.AddRange(result.Variables); return(new ConstructedValueExpression(expressions, arrayVar, variables)); }
public ConstructedValueExpression Compile(MapTypeContext context) { if (!IsSupportedType(context.TargetType)) { return(ConstructedValueExpression.Nothing); } var genericArguments = context.TargetType.GetGenericArguments(); Debug.Assert(genericArguments.Length == 2); var keyType = typeof(string); Debug.Assert(genericArguments[0] == typeof(string)); var elementType = context.TargetType.GetGenericArguments()[1]; Debug.Assert(elementType != null); var typeInfo = GetConcreteTypeInfo(context, keyType, elementType); var dictType = typeInfo.ConcreteType; Debug.Assert(dictType != null); var constructor = typeInfo.Constructor; Debug.Assert(constructor != null); var addMethod = typeInfo.AddMethod; Debug.Assert(addMethod != null); var concreteState = context.ChangeTargetType(dictType); var dictVar = DictionaryExpressionFactory.GetMaybeInstantiateDictionaryExpression(concreteState, constructor); var addStmts = DictionaryExpressionFactory.AddDictionaryPopulateStatements(_valueCompiler, concreteState, elementType, dictVar.FinalValue, addMethod); return(new ConstructedValueExpression( dictVar.Expressions.Concat(addStmts.Expressions), Expression.Convert(dictVar.FinalValue, context.TargetType), dictVar.Variables.Concat(addStmts.Variables) )); }
public ConstructedValueExpression Compile(MapTypeContext context) { if (!IsSupportedType(context.TargetType)) { return(ConstructedValueExpression.Nothing); } var idictType = GetIDictionaryType(context); var elementType = idictType.GetGenericArguments()[1]; var constructor = GetDictionaryConstructor(context.TargetType); var addMethod = GetIDictionaryAddMethod(context.TargetType, idictType); var dictVar = DictionaryExpressionFactory.GetMaybeInstantiateDictionaryExpression(context, constructor); var addStmts = DictionaryExpressionFactory.AddDictionaryPopulateStatements(_valueCompiler, context, elementType, dictVar.FinalValue, addMethod); return(new ConstructedValueExpression( dictVar.Expressions.Concat(addStmts.Expressions), Expression.Convert(dictVar.FinalValue, context.TargetType), dictVar.Variables.Concat(addStmts.Variables))); }
private ConstructedValueExpression GetConstructorCallExpression(MapTypeContext context, ISpecificTypeSettings specificTypeSettings, ParameterExpression instanceVar) { var expressions = new List <Expression>(); var variables = new List <ParameterExpression>(); var constructor = context.GetConstructor(specificTypeSettings); var parameters = constructor.GetParameters(); var args = new Expression[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { var parameter = parameters[i]; var name = parameter.Name.ToLowerInvariant(); var substate = context.GetSubstateForProperty(name, parameter, parameter.ParameterType); // Compile an expression to get a value for this argument. If we don't have, it, // get a default expression for this type. We should have it in theory, because the // constructor finder shouldn't have given us a constructor which contains // parameters we don't have values for. var expr = _values.Compile(substate); if (expr.FinalValue == null || expr.IsNothing) { args[i] = parameter.ParameterType.GetDefaultValueExpression(); continue; } args[i] = expr.FinalValue; expressions.AddRange(expr.Expressions); variables.AddRange(expr.Variables); } // Call the constructor with the given args list expressions.Add( Expression.Assign(instanceVar, Expression.New(constructor, args) ) ); return(new ConstructedValueExpression(expressions, instanceVar, variables)); }
private void MapSupportedPrimitiveType(MapTypeContext context, Expression instance, PropertyInfo property, string name, List <Expression> expressions, List <ParameterExpression> variables) { var propertyState = context.GetSubstateForProperty(name, property, property.PropertyType); var primitiveValueExpression = _scalars.Compile(propertyState); if (primitiveValueExpression.FinalValue == null) { return; } expressions.AddRange(primitiveValueExpression.Expressions); expressions.Add( Expression.Call( instance, property.GetSetMethod(), Expression.Convert( primitiveValueExpression.FinalValue, property.PropertyType ) ) ); variables.AddRange(primitiveValueExpression.Variables); }
private ConcreteCollectionInfo GetConcreteTypeInfoForGenericInterfaceType(MapTypeContext context) { // We have one of the interfaces IEnumerable<T>, ICollection<T>, // IReadOnlyCollection<T>, IList<T> or IReadOnlyList<T>, get T from generic type // definition var elementType = context.TargetType.GenericTypeArguments[0]; Type collectionType = null; // See if we have a custom type configured which we should use instead of List<T> var typeSettings = context.Settings.GetTypeSettings(context.TargetType); if (typeSettings.BaseType != null && typeSettings.BaseType != typeof(object)) { collectionType = typeSettings.GetDefault().Type; } // Just use List<T> if there is no custom type if (collectionType == null) { return(GetDefaultConcreteTypeInfo(context.TargetType, elementType)); } // The type should have an Add(T) method (even if the interface we're using is // IReadOnly...<T>). The Add method might not be exposed through the interface. var addMethod = collectionType.GetMethods(BindingFlags.Public | BindingFlags.Instance) .FirstOrDefault(m => m.Name == nameof(ICollection <object> .Add) && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == elementType); var constructor = collectionType.GetConstructor(new Type[0]); // Fall back to List<T> if the custom type isn't working out. if (addMethod == null || constructor == null) { return(GetDefaultConcreteTypeInfo(context.TargetType, elementType)); } return(new ConcreteCollectionInfo(collectionType, elementType, constructor, addMethod)); }
private ConcreteCollectionInfo GetConcreteTypeInfoForInterfaceType(MapTypeContext context) { // It's one of IEnumerable, ICollection or IList, so we will do some searching to find // an appropriate elementType and Add() method variant var typeSettings = context.Settings.GetTypeSettings(context.TargetType); if (typeSettings.BaseType == typeof(object)) { return(GetDefaultConcreteTypeInfo(context.TargetType, typeof(object))); } var customType = typeSettings.GetDefault().Type; var constructor = customType.GetConstructor(new Type[0]); // The custom type might have multiple .Add() method variants. We have to try to pick // the "Best" one, which is hard to do. Basically we want Add(<anyType>) instead of // Add(object), if possible. var addMethods = customType .GetMethods(BindingFlags.Public | BindingFlags.Instance) .Where(m => m.Name == nameof(ICollection <object> .Add) && m.GetParameters().Length == 1) .Select(m => new { AddMethod = m, Parameter = m.GetParameters()[0] }) .OrderBy(x => x.Parameter.ParameterType == typeof(object) ? 0 : 1) .ToList(); // If we don't have a default constructor or a suitable .Add() method, fall back to // List<object>. There's no way to signal an error from here, so the user has to // figure it out. if (constructor == null || addMethods.Count == 0) { return(GetDefaultConcreteTypeInfo(context.TargetType, typeof(object))); } var bestAddMethod = addMethods.FirstOrDefault(); return(new ConcreteCollectionInfo(customType, bestAddMethod.Parameter.ParameterType, constructor, bestAddMethod.AddMethod)); }
public ConstructedValueExpression Compile(MapTypeContext context) { if (!context.TargetType.IsMappableCustomObjectType()) { return(ConstructedValueExpression.Nothing); } var expressions = new List <Expression>(); var variables = new List <ParameterExpression>(); // Instantiate the object, using a factory or constructor var initExpr = AddInstantiationExpressionForObjectInstance(context); expressions.AddRange(initExpr.Expressions); variables.AddRange(initExpr.Variables); // Assign public writeable property values var addPropertyStmts = AddPropertyAssignmentExpressions(context, initExpr.FinalValue); expressions.AddRange(addPropertyStmts.Expressions); variables.AddRange(addPropertyStmts.Variables); return(new ConstructedValueExpression(expressions, Expression.Convert(initExpr.FinalValue, context.TargetType), variables)); }
public ConstructedValueExpression Compile(MapTypeContext context) { if (!IsSupportedType(context.TargetType)) { return(ConstructedValueExpression.Nothing); } // We have a concrete type which implements IEnumerable/IEnumerable<T> and has an Add // method. Get these things. var(collectionType, elementType, constructor, addMethod) = GetTypeInfo(context); Debug.Assert(collectionType != null); Debug.Assert(context.TargetType.IsAssignableFrom(collectionType)); Debug.Assert(elementType != null); Debug.Assert(addMethod != null); Debug.Assert(constructor != null); var collectionVar = CollectionExpressionFactory.GetMaybeInstantiateCollectionExpression(context, constructor); var addStmts = CollectionExpressionFactory.GetCollectionPopulateStatements(_scalars, _nonScalars, context, elementType, collectionVar.FinalValue, addMethod); return(new ConstructedValueExpression( collectionVar.Expressions.Concat(addStmts.Expressions), collectionVar.FinalValue, collectionVar.Variables.Concat(addStmts.Variables))); }
public static ConstructedValueExpression GetMaybeInstantiateCollectionExpression(MapTypeContext context, ConstructorInfo constructor) { var newInstance = context.CreateVariable(context.TargetType, "list"); if (context.GetExisting == null) { // if we don't have an expression to get an existing slot, just create new by // calling the default parameterless constructor on the collection type var createNewExpr = Expression.Assign( newInstance, Expression.New(constructor) ); return(new ConstructedValueExpression(new[] { createNewExpr }, newInstance, new[] { newInstance })); } // Try to get the existing value. If it's null, just create new with the default // parameterless constructor. If it's not null we can return it. var getExistingExpr = Expression.Assign( newInstance, Expression.Condition( Expression.Equal( context.GetExisting, Expression.Constant(null) ), Expression.Convert( Expression.New(constructor), context.TargetType ), Expression.Convert(context.GetExisting, context.TargetType) ) ); return(new ConstructedValueExpression(new[] { getExistingExpr }, newInstance, new[] { newInstance })); }
private ConstructedValueExpression AddPropertyAssignmentExpressions(MapTypeContext context, Expression instance) { var expressions = new List <Expression>(); var variables = new List <ParameterExpression>(); // Get the list of public, writeable properties var properties = context.TargetType.GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => p.GetMethod != null) .Where(p => p.CanRead) .Where(p => !p.GetMethod.IsPrivate); foreach (var property in properties) { var name = property.Name.ToLowerInvariant(); // Primitive types cannot be updated in place, they must have a public setter if (property.PropertyType.IsSupportedPrimitiveType()) { if (!property.IsSettable()) { continue; } MapSupportedPrimitiveType(context, instance, property, name, expressions, variables); continue; } // Non-primitive types may have an existing value which can be updated in-place // by reference. We pass this getPropExpr expression to the substate as the // getExisting expression. If this exists, all the generated expressions will // update the existing value in-place. var getPropExpr = Expression.Property(instance, property); var substate = context.GetSubstateForProperty(name, property, property.PropertyType, getPropExpr); var expression = _values.Compile(substate); if (expression.Expressions == null) { continue; } expressions.AddRange(expression.Expressions); variables.AddRange(expression.Variables); // If the property value is null but there is no setter, we may still populate an object // but just ignore it. That seems rare but still sub-optimal. if (!property.IsSettable()) { continue; } // Set the value, if it has a setter expressions.Add( Expression.Call( instance, property.GetSetMethod(), Expression.Convert( expression.FinalValue, property.PropertyType ) ) ); } return(new ConstructedValueExpression(expressions, null, variables)); }
public static ConstructedValueExpression GetMaybeInstantiateDictionaryExpression(MapTypeContext context, ConstructorInfo constructor) { var newInstance = context.CreateVariable(context.TargetType, "dictionary"); if (context.GetExisting == null) { // There's no way to get an existing value, so call the parameterless constructor var getNewInstanceExpr = Expression.Assign( newInstance, Expression.New(constructor) ); return(new ConstructedValueExpression(new[] { getNewInstanceExpr }, newInstance, new[] { newInstance })); } // Try to get the existing value. If we have it return it. Otherwise call the default // parameterless constructor var tryGetExistingInstanceExpr = Expression.Assign( newInstance, Expression.Condition( Expression.Equal( context.GetExisting, Expression.Constant(null) ), Expression.Convert( Expression.New(constructor), context.TargetType ), Expression.Convert(context.GetExisting, context.TargetType) ) ); return(new ConstructedValueExpression(new[] { tryGetExistingInstanceExpr }, newInstance, new[] { newInstance })); }
private ConstructedValueExpression AddInstantiationExpressionForObjectInstance(MapTypeContext context) { var instanceVar = context.CreateVariable(context.TargetType, "instance"); var typeInfo = context.Settings.GetTypeSettings(context.TargetType); var defaultSubtypeInfo = typeInfo.GetDefault(); var defaultInstantiationExpression = AddInstantiationExpressionForSpecificType(context, defaultSubtypeInfo, instanceVar); var subclasses = typeInfo.GetSpecificTypes().ToList(); // If we have no configured subclass predicates, instantiate the target type directly if (subclasses.Count == 0) { return(new ConstructedValueExpression(defaultInstantiationExpression.Expressions, instanceVar, defaultInstantiationExpression.Variables.Concat(new[] { instanceVar }))); } // If we have configured subclass predicates, unroll the loop and write expressions for // all cases. The default case will be executed if no subclass predicates match. var endLabel = context.CreateLabel("haveinstance"); var expressions = new List <Expression>(); foreach (var subclass in subclasses.Where(s => s.Predicate != null)) { var instantiateExpressions = AddInstantiationExpressionForSpecificType(context, subclass, instanceVar); // Check the subclass predicate. If it matches, we enter into a block where we // create the subclass instance and then jump to the end label. Otherwise we // fall through to the next subclass. expressions.Add( Expression.IfThen( Expression.Call( Expression.Constant(subclass.Predicate.Target), subclass.Predicate.Method, context.RecordParameter ), Expression.Block( instantiateExpressions.Variables, instantiateExpressions.Expressions.Concat(new[] { Expression.Goto(endLabel.Target) }) ) ) ); } // A block where we fall back to the default instantiation rules for the type, and then // fall through to the endLabel. The block here allows us to scope variables a little // more tightly expressions.Add( Expression.Block( defaultInstantiationExpression.Variables, defaultInstantiationExpression.Expressions ) ); expressions.Add(endLabel); return(new ConstructedValueExpression(expressions, instanceVar, new[] { instanceVar })); }
public static ConstructedValueExpression AddDictionaryPopulateStatements(ICompiler values, MapTypeContext context, Type elementType, Expression dictVar, MethodInfo addMethod) { var expressions = new List <Expression>(); var variables = new List <ParameterExpression>(); if (context.Name == null) { var firstColumnsByName = context.GetFirstIndexForEachColumnName(); foreach (var column in firstColumnsByName) { var substate = context.GetSubstateForProperty(column.CanonicalName, null, elementType); var getScalarExpression = values.Compile(substate); expressions.AddRange(getScalarExpression.Expressions); expressions.Add( Expression.Call( dictVar, addMethod, Expression.Constant(column.OriginalName), getScalarExpression.FinalValue ) ); variables.AddRange(getScalarExpression.Variables); } return(new ConstructedValueExpression(expressions, null, variables)); } var columns = context.GetFirstIndexForEachColumnName(); foreach (var column in columns) { var keyName = column.OriginalName.Substring(context.CurrentPrefix.Length); var childName = column.CanonicalName.Substring(context.CurrentPrefix.Length); var columnSubstate = context.GetSubstateForColumn(column, elementType, childName); var getScalarExpression = values.Compile(columnSubstate); expressions.AddRange(getScalarExpression.Expressions); expressions.Add( Expression.Call( dictVar, addMethod, Expression.Constant(keyName), getScalarExpression.FinalValue ) ); variables.AddRange(getScalarExpression.Variables); } return(new ConstructedValueExpression(expressions, null, variables)); }
public ConstructedValueExpression Compile(MapTypeContext context) => _getCompiler().Compile(context);
public static ConstructedValueExpression GetCollectionPopulateStatements(ICompiler scalars, ICompiler nonScalars, MapTypeContext context, Type elementType, Expression listVar, MethodInfo addMethod) { if (!context.HasColumns()) { return(new ConstructedValueExpression(null)); } var expressions = new List <Expression>(); var variables = new List <ParameterExpression>(); // For scalar types (object is treated like a scalar here), map each column to an entry in the // array, consuming all remaining columns. if (elementType.IsSupportedPrimitiveType() || elementType == typeof(object)) { var columns = context.GetColumns(); foreach (var column in columns) { var columnState = context.GetSubstateForColumn(column, elementType, null); var result = scalars.Compile(columnState); expressions.AddRange(result.Expressions); expressions.Add( Expression.Call( listVar, addMethod, result.FinalValue ) ); variables.AddRange(result.Variables); } return(new ConstructedValueExpression(expressions, null, variables)); } // For all non-scalar types // loop so long as we have columns and consume columns every iteration. var elementState = context.ChangeTargetType(elementType); var numberOfColumns = context.NumberOfColumns(); while (numberOfColumns > 0) { var result = nonScalars.Compile(elementState); var newNumberOfColumns = context.NumberOfColumns(); // We haven't consumed any new columns, so we're done. if (newNumberOfColumns == numberOfColumns) { break; } expressions.AddRange(result.Expressions); expressions.Add( Expression.Call(listVar, addMethod, result.FinalValue) ); variables.AddRange(result.Variables); numberOfColumns = newNumberOfColumns; } return(new ConstructedValueExpression(expressions, null, variables)); }