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)); }
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) )); }
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)); }
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)); }
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) )); }