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