コード例 #1
0
        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));
        }
コード例 #2
0
        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 }));
        }
コード例 #3
0
        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));
        }
コード例 #4
0
        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)
                       ));
        }
コード例 #5
0
 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));
 }
コード例 #6
0
        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);
        }
コード例 #7
0
        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));
        }
コード例 #8
0
        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));
        }
コード例 #9
0
 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);
 }
コード例 #10
0
        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));
        }
コード例 #11
0
        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));
        }
コード例 #12
0
        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));
        }
コード例 #13
0
        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));
        }
コード例 #14
0
        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));
        }
コード例 #15
0
        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));
        }
コード例 #16
0
        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)
                       ));
        }
コード例 #17
0
        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)));
        }
コード例 #18
0
        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));
        }
コード例 #19
0
        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);
        }
コード例 #20
0
        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));
        }
コード例 #21
0
        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));
        }
コード例 #22
0
        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));
        }
コード例 #23
0
        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)));
        }
コード例 #24
0
        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 }));
        }
コード例 #25
0
        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));
        }
コード例 #26
0
        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 }));
        }
コード例 #27
0
        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 }));
        }
コード例 #28
0
        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));
        }
コード例 #29
0
 public ConstructedValueExpression Compile(MapTypeContext context) => _getCompiler().Compile(context);
コード例 #30
0
        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));
        }