Exemple #1
0
        internal Expression ReduceAssign(Func <Expression, Expression> assign)
        {
            var temps = new List <ParameterExpression>();
            var stmts = new List <Expression>();

            var arrayAccess = ReduceAssign(temps, stmts);

            stmts.Add(assign(arrayAccess));

            return(Helpers.Comma(temps, stmts));
        }
        /// <summary>
        /// Reduces the expression node to a simpler expression.
        /// </summary>
        /// <returns>The reduced expression.</returns>
        public override Expression Reduce()
        {
            if (IsSimpleArrayAccess())
            {
                return(ReduceSimple());
            }

            if (Indexes.Count == 1)
            {
                var indexType = Indexes[0].Type;

                if (indexType == typeof(Index))
                {
                    return(ReduceIndex());
                }
                else if (indexType == typeof(Range))
                {
                    return(ReduceRange());
                }
            }

            throw ContractUtils.Unreachable;

            Expression ReduceIndex()
            {
                // REVIEW: Evaluate array.Length before index argument? May throw NullReferenceException.

                var temps = new List <ParameterExpression>(1);
                var stmts = new List <Expression>(2);

                var array = GetArrayExpression(temps, stmts);

                var index = GetIntIndexExpression(array, Indexes[0]);

                stmts.Add(Expression.ArrayAccess(array, index));

                return(Helpers.Comma(temps, stmts));
            }

            Expression ReduceRange()
            {
                // System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray(array, Range)

                var elemType          = Array.Type.GetElementType(); // REVIEW
                var getSubArrayMethod = GetSubArrayMethod.MakeGenericMethod(elemType);

                return(Expression.Call(getSubArrayMethod, Array, Indexes[0]));
            }
        }
        private Expression ReduceWithClone(Expression clone)
        {
            var stmts = new List <Expression>(Initializers.Count + 2);

            var obj = Expression.Variable(Object.Type, "__obj");

            stmts.Add(Expression.Assign(obj, clone));

            ReduceMemberInitializers(obj, stmts);

            stmts.Add(obj);

            return(Helpers.Comma(new List <ParameterExpression>(1)
            {
                obj
            }, stmts));
        }
        private Expression ReduceAnonymousType()
        {
            var initCount = Initializers.Count;

            var vars  = new List <ParameterExpression>(initCount + 1);
            var stmts = new List <Expression>(initCount + 2);

            var obj = Expression.Variable(Object.Type, "__obj");

            vars.Add(obj);
            stmts.Add(Expression.Assign(obj, Object));

            var memberExpressions = new Dictionary <MemberInfo, Expression>();

            for (var i = 0; i < initCount; i++)
            {
                var initializer = Initializers[i];
                var expr        = initializer.Expression;

                if (!expr.IsPure())
                {
                    var tmp = Expression.Variable(expr.Type, "__init" + i);

                    vars.Add(tmp);
                    stmts.Add(Expression.Assign(tmp, expr));

                    expr = tmp;
                }

                //
                // NB: If there are duplicate assignments, the last one wins. We do not check for uniqueness,
                //     which is similar to MemberInitExpression.
                //

                memberExpressions[initializer.Member] = expr;
            }

            var memberCount = Members.Count;

            var args        = new Expression[memberCount];
            var memberTypes = new Type[memberCount];

            for (var i = 0; i < memberCount; i++)
            {
                var member = Members[i];

                memberTypes[i] = member is PropertyInfo p ? p.PropertyType : ((FieldInfo)member).FieldType;

                if (memberExpressions.TryGetValue(member, out var expr))
                {
                    args[i] = expr;
                }
                else
                {
                    args[i] = Expression.MakeMemberAccess(obj, member);
                }
            }

            var ctor = Object.Type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, binder: null, memberTypes, modifiers: null);

            stmts.Add(Expression.New(ctor, args, Members));

            return(Helpers.Comma(vars, stmts));
        }
        internal Expression ReduceAssign(Func <Expression, Expression> assign)
        {
            var firstIndex = Indexes[0];
            var indexType  = firstIndex.Type;

            if (Indexes.Count > 1 || indexType == typeof(int))
            {
                var temps = new List <ParameterExpression>(Indexes.Count + 1);
                var stmts = new List <Expression>(Indexes.Count + 3);

                var array = GetArrayExpression(temps, stmts);

                var indexes = new List <Expression>(Indexes.Count);

                for (int i = 0, n = Indexes.Count; i < n; i++)
                {
                    var index = Indexes[i];

                    if (Helpers.IsPure(index))
                    {
                        indexes.Add(index);
                    }
                    else
                    {
                        var indexVariable = Expression.Parameter(index.Type, "__idx" + i);

                        temps.Add(indexVariable);
                        stmts.Add(Expression.Assign(indexVariable, index));

                        indexes.Add(indexVariable);
                    }
                }

                var access = assign(Expression.ArrayAccess(array, indexes));
                stmts.Add(access);

                return(Helpers.Comma(temps, stmts));
            }

            if (indexType == typeof(Index))
            {
                var temps = new List <ParameterExpression>(2);
                var stmts = new List <Expression>(3);

                var array = GetArrayExpression(temps, stmts);

                var index = GetIntIndexExpression(array, firstIndex);

                if (!Helpers.IsPure(index))
                {
                    var indexVariable = Expression.Parameter(typeof(int), "__idx");

                    temps.Add(indexVariable);
                    stmts.Add(Expression.Assign(indexVariable, index));

                    index = indexVariable;
                }

                // NB: We don't have ref locals in expression trees, so we may end up with
                //
                //     receiver[index] = ... receiver[index] ...
                //
                //     for compound assignments, which is benign but may incur multiple bounds checks. Alternatively, we could
                //     dispatch into the RuntimeOpsEx.WithByRef helper method.

                var access = assign(Expression.ArrayAccess(array, index));
                stmts.Add(access);

                return(Helpers.Comma(temps, stmts));
            }

            throw ContractUtils.Unreachable;
        }
        /// <summary>
        /// Reduces the expression node to a simpler expression.
        /// </summary>
        /// <returns>The reduced expression.</returns>
        public override Expression Reduce()
        {
            var left  = RemoveNullableIfNeverNull(Left);
            var right = RemoveNullableIfNeverNull(Right);

            var temps = new List <ParameterExpression>();
            var stmts = new List <Expression>();

            left  = SpillToTemps(left, EqualityChecks, "__left");
            right = SpillToTemps(right, EqualityChecks, "__right");

            stmts.Add(Reduce(left, right, EqualityChecks));

            return(Helpers.Comma(temps, stmts));

            Expression SpillToTemps(Expression tuple, ReadOnlyCollection <LambdaExpression> equalityChecks, string prefix)
            {
                //
                // CONSIDER: Handle TupleConvertCSharpExpression by analyzing if all conversions are implicit, unchecked, and non-user-defined
                //           ones, in which case we can spill the operand to a temp and defer applying the conversions to elements.
                //

                if (tuple is TupleLiteralCSharpExpression literal)
                {
                    List <Expression> newArgs = null;

                    var args = literal.Arguments;

                    void EnsureArgs(int max)
                    {
                        if (newArgs == null)
                        {
                            newArgs = new List <Expression>(args.Count);

                            for (int j = 0; j < max; j++)
                            {
                                newArgs.Add(args[j]);
                            }
                        }
                    }

                    for (int i = 0, n = args.Count; i < n; i++)
                    {
                        var arg = args[i];

                        if (Helpers.IsPure(arg))
                        {
                            if (newArgs != null)
                            {
                                newArgs.Add(arg);
                            }
                        }
                        else if (arg is TupleLiteralCSharpExpression nestedLiteral && IsNestedTupleBinaryEqualityCheck(CSharpNodeType, equalityChecks[i], out var nestedEqualityChecks))
                        {
                            var newArg = SpillToTemps(nestedLiteral, nestedEqualityChecks, prefix + i + "_");

                            if (newArg != arg)
                            {
                                EnsureArgs(i);
                            }

                            if (newArgs != null)
                            {
                                newArgs.Add(newArg);
                            }
                        }
                        else
                        {
                            var tmp = Expression.Parameter(arg.Type, prefix + i);
                            temps.Add(tmp);
                            stmts.Add(Expression.Assign(tmp, arg));

                            EnsureArgs(i);
                            newArgs.Add(tmp);
                        }
                    }

                    return(newArgs != null?literal.Update(newArgs) : literal);
                }
        /// <summary>
        /// Reduces the expression node to a simpler expression.
        /// </summary>
        /// <returns>The reduced expression.</returns>
        public override Expression Reduce()
        {
            var operand = Operand;

            var temps = new List<ParameterExpression>();
            var stmts = new List<Expression>();


            //
            // NB: We can't check IsPure with the readOnly parameter set to true, because the user conversions may assign to a variable.
            //     To mitigate this, we'd have to check all element conversions to make sure they don't assign to the variable, which is
            //     very unlikely, though a hand-written conversion lambda could have such a side-effect. One could argue that a side-
            //     effecting conversion that mutates the parent tuple yields undefined behavior.
            //

            if (!Helpers.IsPure(Operand))
            {
                var operandVariable = Expression.Parameter(Operand.Type, "__t");
                
                temps.Add(operandVariable);
                stmts.Add(Expression.Assign(operandVariable, Operand));

                operand = operandVariable;
            }

            Expression res;

            if (operand.Type.IsNullableType())
            {
                var nonNullOperand = Helpers.MakeNullableGetValue(operand); // NB: Use of Nullable<T>.Value to ensure proper exception is thrown if null.
                var nonNullOperandVariable = Expression.Parameter(nonNullOperand.Type, "__nonNull");

                var args = GetConversions(nonNullOperandVariable);

                if (Type.IsNullableType())
                {
                    //
                    // T? -> U?
                    //
                    // var t = operand;
                    //
                    // if (t.HasValue)
                    // {
                    //    var v = t.Value;
                    //    return (U?)new U(...);
                    // }
                    // else
                    // {
                    //    return default(U?);
                    // }
                    //

                    var hasValueTest = Helpers.MakeNullableHasValue(operand);

                    var nullValue = Expression.Default(Type);

                    var convertedTuple = Expression.Convert(CSharpExpression.TupleLiteral(Type.GetNonNullableType(), args, argumentNames: null), Type);
                    var nonNullValue = Expression.Block(new[] { nonNullOperandVariable }, Expression.Assign(nonNullOperandVariable, nonNullOperand), convertedTuple);

                    res = Expression.Condition(hasValueTest, nonNullValue, nullValue);
                }
                else
                {
                    //
                    // T? -> U
                    //
                    // var t = operand;
                    // var v = operand.Value; // NB: May throw
                    // return new U(...);

                    temps.Add(nonNullOperandVariable);
                    stmts.Add(Expression.Assign(nonNullOperandVariable, nonNullOperand));

                    res = CSharpExpression.TupleLiteral(Type, args, argumentNames: null);
                }
            }
            else
            {
                var args = GetConversions(operand);

                var targetType = Type.GetNonNullableType();

                var convertedTuple = CSharpExpression.TupleLiteral(targetType, args, argumentNames: null);

                if (Type.IsNullableType())
                {
                    //
                    // T -> U?
                    //
                    // var t = operand;
                    // return (U?)new U(...);

                    res = Expression.Convert(convertedTuple, Type);
                }
                else
                {
                    //
                    // T -> U
                    //
                    // var t = operand;
                    // return new U(...);
                    //

                    res = convertedTuple;
                }
            }

            stmts.Add(res);

            return Helpers.Comma(temps, stmts);

            List<Expression> GetConversions(Expression operand)
            {
                var n = ElementConversions.Count;

                var args = new List<Expression>(n);

                for (int i = 0; i < n; i++)
                {
                    var conversion = ElementConversions[i];

                    var item = Helpers.GetTupleItemAccess(operand, i);

                    var conversionParameter = conversion.Parameters[0];

                    if (conversion.Body is UnaryExpression { Operand: var unaryOperand } unary && unaryOperand == conversionParameter && IsConvert(unary.NodeType))
                    {
                        args.Add(unary.Update(item));
                    }