1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Collections.Generic; 6 using System.Reflection; 7 using Xunit; 8 9 namespace System.Linq.Expressions.Tests 10 { 11 public class OpAssign 12 { 13 [Theory] 14 [PerCompilationType(nameof(AssignAndEquivalentMethods))] AssignmentEquivalents(MethodInfo nonAssign, MethodInfo assign, Type type, bool useInterpreter)15 public void AssignmentEquivalents(MethodInfo nonAssign, MethodInfo assign, Type type, bool useInterpreter) 16 { 17 Func<Expression, Expression, Expression> withoutAssignment = (Func<Expression, Expression, Expression>)nonAssign.CreateDelegate(typeof(Func<Expression, Expression, Expression>)); 18 Func<Expression, Expression, Expression> withAssignment = (Func<Expression, Expression, Expression>)assign.CreateDelegate(typeof(Func<Expression, Expression, Expression>)); 19 20 foreach (object x in new[] { 0, -1, 1, 10 }.Select(i => Convert.ChangeType(i, type))) 21 foreach (object y in new[] { -1, 1, 10 }.Select(i => Convert.ChangeType(i, type))) 22 { 23 ConstantExpression xExp = Expression.Constant(x); 24 ConstantExpression yExp = Expression.Constant(y); 25 Expression woAssign = withoutAssignment(xExp, yExp); 26 ParameterExpression variable = Expression.Variable(type); 27 Expression initAssign = Expression.Assign(variable, xExp); 28 Expression assignment = withAssignment(variable, yExp); 29 Expression wAssign = Expression.Block( 30 new ParameterExpression[] { variable }, 31 initAssign, 32 assignment 33 ); 34 Assert.True(Expression.Lambda<Func<bool>>(Expression.Equal(woAssign, wAssign)).Compile(useInterpreter)()); 35 LabelTarget target = Expression.Label(type); 36 Expression wAssignReturningVariable = Expression.Block( 37 new ParameterExpression[] { variable }, 38 initAssign, 39 assignment, 40 Expression.Return(target, variable), 41 Expression.Label(target, Expression.Default(type)) 42 ); 43 Assert.True(Expression.Lambda<Func<bool>>(Expression.Equal(woAssign, wAssignReturningVariable)).Compile(useInterpreter)()); 44 } 45 } 46 47 private class Box<T> 48 { 49 public static T StaticValue { get; set; } 50 public T Value { get; set; } 51 public T this[int index] 52 { 53 get 54 { 55 return Value; 56 } 57 set 58 { 59 Value = value; 60 } 61 } Box(T value)62 public Box(T value) 63 { 64 Value = value; 65 } 66 } 67 68 [Theory] 69 [PerCompilationType(nameof(AssignAndEquivalentMethods))] AssignmentEquivalentsWithMemberAccess(MethodInfo nonAssign, MethodInfo assign, Type type, bool useInterpreter)70 public void AssignmentEquivalentsWithMemberAccess(MethodInfo nonAssign, MethodInfo assign, Type type, bool useInterpreter) 71 { 72 Func<Expression, Expression, Expression> withoutAssignment = (Func<Expression, Expression, Expression>)nonAssign.CreateDelegate(typeof(Func<Expression, Expression, Expression>)); 73 Func<Expression, Expression, Expression> withAssignment = (Func<Expression, Expression, Expression>)assign.CreateDelegate(typeof(Func<Expression, Expression, Expression>)); 74 75 foreach (object x in new[] { 0, -1, 1, 10 }.Select(i => Convert.ChangeType(i, type))) 76 foreach (object y in new[] { -1, 1, 10 }.Select(i => Convert.ChangeType(i, type))) 77 { 78 ConstantExpression xExp = Expression.Constant(x); 79 ConstantExpression yExp = Expression.Constant(y); 80 Expression woAssign = withoutAssignment(xExp, yExp); 81 Type boxType = typeof(Box<>).MakeGenericType(type); 82 object box = boxType.GetConstructor(new[] { type }).Invoke(new object[] { x }); 83 Expression boxExp = Expression.Constant(box); 84 Expression property = Expression.Property(boxExp, boxType.GetProperty("Value")); 85 Expression assignment = withAssignment(property, yExp); 86 Assert.True(Expression.Lambda<Func<bool>>(Expression.Equal(woAssign, assignment)).Compile(useInterpreter)()); 87 LabelTarget target = Expression.Label(type); 88 box = boxType.GetConstructor(new[] { type }).Invoke(new object[] { x }); 89 boxExp = Expression.Constant(box); 90 property = Expression.Property(boxExp, boxType.GetProperty("Value")); 91 assignment = withAssignment(property, yExp); 92 Expression wAssignReturningVariable = Expression.Block( 93 assignment, 94 Expression.Return(target, property), 95 Expression.Label(target, Expression.Default(type)) 96 ); 97 Assert.True(Expression.Lambda<Func<bool>>(Expression.Equal(woAssign, wAssignReturningVariable)).Compile(useInterpreter)()); 98 } 99 } 100 101 [Theory, PerCompilationType(nameof(AssignAndEquivalentMethods))] AssignmentEquivalentsWithStaticMemberAccess(MethodInfo nonAssign, MethodInfo assign, Type type, bool useInterpreter)102 public void AssignmentEquivalentsWithStaticMemberAccess(MethodInfo nonAssign, MethodInfo assign, Type type, bool useInterpreter) 103 { 104 Func<Expression, Expression, Expression> withoutAssignment = (Func<Expression, Expression, Expression>)nonAssign.CreateDelegate(typeof(Func<Expression, Expression, Expression>)); 105 Func<Expression, Expression, Expression> withAssignment = (Func<Expression, Expression, Expression>)assign.CreateDelegate(typeof(Func<Expression, Expression, Expression>)); 106 107 foreach (object x in new[] { 0, -1, 1, 10 }.Select(i => Convert.ChangeType(i, type))) 108 foreach (object y in new[] { -1, 1, 10 }.Select(i => Convert.ChangeType(i, type))) 109 { 110 ConstantExpression xExp = Expression.Constant(x); 111 ConstantExpression yExp = Expression.Constant(y); 112 Expression woAssign = withoutAssignment(xExp, yExp); 113 Type boxType = typeof(Box<>).MakeGenericType(type); 114 PropertyInfo prop = boxType.GetProperty("StaticValue"); 115 prop.SetValue(null, x); 116 Expression property = Expression.Property(null, prop); 117 Expression assignment = withAssignment(property, yExp); 118 Assert.True(Expression.Lambda<Func<bool>>(Expression.Equal(woAssign, assignment)).Compile(useInterpreter)()); 119 prop.SetValue(null, x); 120 Expression wAssignReturningVariable = Expression.Block( 121 assignment, 122 property 123 ); 124 Assert.True(Expression.Lambda<Func<bool>>(Expression.Equal(woAssign, wAssignReturningVariable)).Compile(useInterpreter)()); 125 } 126 } 127 [Theory] 128 [PerCompilationType(nameof(AssignAndEquivalentMethods))] AssignmentEquivalentsWithIndexAccess(MethodInfo nonAssign, MethodInfo assign, Type type, bool useInterpreter)129 public void AssignmentEquivalentsWithIndexAccess(MethodInfo nonAssign, MethodInfo assign, Type type, bool useInterpreter) 130 { 131 Func<Expression, Expression, Expression> withoutAssignment = (Func<Expression, Expression, Expression>)nonAssign.CreateDelegate(typeof(Func<Expression, Expression, Expression>)); 132 Func<Expression, Expression, Expression> withAssignment = (Func<Expression, Expression, Expression>)assign.CreateDelegate(typeof(Func<Expression, Expression, Expression>)); 133 134 foreach (object x in new[] { 0, -1, 1, 10 }.Select(i => Convert.ChangeType(i, type))) 135 foreach (object y in new[] { -1, 1, 10 }.Select(i => Convert.ChangeType(i, type))) 136 { 137 ConstantExpression xExp = Expression.Constant(x); 138 ConstantExpression yExp = Expression.Constant(y); 139 Expression woAssign = withoutAssignment(xExp, yExp); 140 Type boxType = typeof(Box<>).MakeGenericType(type); 141 object box = boxType.GetConstructor(new[] { type }).Invoke(new object[] { x }); 142 Expression boxExp = Expression.Constant(box); 143 Expression property = Expression.Property(boxExp, boxType.GetProperty("Item"), Expression.Constant(0)); 144 Expression assignment = withAssignment(property, yExp); 145 Assert.True(Expression.Lambda<Func<bool>>(Expression.Equal(woAssign, assignment)).Compile(useInterpreter)()); 146 LabelTarget target = Expression.Label(type); 147 box = boxType.GetConstructor(new[] { type }).Invoke(new object[] { x }); 148 boxExp = Expression.Constant(box); 149 property = Expression.Property(boxExp, boxType.GetProperty("Item"), Expression.Constant(0)); 150 assignment = withAssignment(property, yExp); 151 Expression wAssignReturningVariable = Expression.Block( 152 assignment, 153 Expression.Return(target, property), 154 Expression.Label(target, Expression.Default(type)) 155 ); 156 Assert.True(Expression.Lambda<Func<bool>>(Expression.Equal(woAssign, wAssignReturningVariable)).Compile(useInterpreter)()); 157 } 158 } 159 160 [Theory] 161 [MemberData(nameof(AssignmentMethods))] AssignmentReducable(MethodInfo assign, Type type)162 public void AssignmentReducable(MethodInfo assign, Type type) 163 { 164 Func<Expression, Expression, Expression> withAssignment = (Func<Expression, Expression, Expression>)assign.CreateDelegate(typeof(Func<Expression, Expression, Expression>)); 165 166 ParameterExpression variable = Expression.Variable(type); 167 Expression assignment = withAssignment(variable, Expression.Default(type)); 168 Assert.True(assignment.CanReduce); 169 Assert.NotSame(assignment, assignment.ReduceAndCheck()); 170 } 171 172 [Theory] 173 [MemberData(nameof(AssignmentMethods))] CannotAssignToNonWritable(MethodInfo assign, Type type)174 public void CannotAssignToNonWritable(MethodInfo assign, Type type) 175 { 176 Func<Expression, Expression, Expression> withAssignment = (Func<Expression, Expression, Expression>)assign.CreateDelegate(typeof(Func<Expression, Expression, Expression>)); 177 178 AssertExtensions.Throws<ArgumentException>("left", () => withAssignment(Expression.Default(type), Expression.Default(type))); 179 } 180 181 [Theory] 182 [MemberData(nameof(AssignmentMethods))] AssignmentWithMemberAccessReducable(MethodInfo assign, Type type)183 public void AssignmentWithMemberAccessReducable(MethodInfo assign, Type type) 184 { 185 Func<Expression, Expression, Expression> withAssignment = (Func<Expression, Expression, Expression>)assign.CreateDelegate(typeof(Func<Expression, Expression, Expression>)); 186 187 Type boxType = typeof(Box<>).MakeGenericType(type); 188 object box = boxType.GetConstructor(new[] { type }).Invoke(new object[] { Convert.ChangeType(0, type) }); 189 Expression boxExp = Expression.Constant(box); 190 Expression property = Expression.Property(boxExp, boxType.GetProperty("Value")); 191 Expression assignment = withAssignment(property, Expression.Default(type)); 192 Assert.True(assignment.CanReduce); 193 Assert.NotSame(assignment, assignment.ReduceAndCheck()); 194 } 195 196 [Theory] 197 [MemberData(nameof(AssignmentMethods))] AssignmentWithIndexAccessReducable(MethodInfo assign, Type type)198 public void AssignmentWithIndexAccessReducable(MethodInfo assign, Type type) 199 { 200 Func<Expression, Expression, Expression> withAssignment = (Func<Expression, Expression, Expression>)assign.CreateDelegate(typeof(Func<Expression, Expression, Expression>)); 201 202 Type boxType = typeof(Box<>).MakeGenericType(type); 203 object box = boxType.GetConstructor(new[] { type }).Invoke(new object[] { Convert.ChangeType(0, type) }); 204 Expression boxExp = Expression.Constant(box); 205 Expression property = Expression.Property(boxExp, boxType.GetProperty("Item"), Expression.Constant(0)); 206 Expression assignment = withAssignment(property, Expression.Default(type)); 207 Assert.True(assignment.CanReduce); 208 Assert.NotSame(assignment, assignment.ReduceAndCheck()); 209 } 210 211 212 private static class Unreadable<T> 213 { 214 public static T WriteOnly 215 { 216 set { } 217 } 218 } 219 220 [Theory] 221 [MemberData(nameof(AssignmentMethods))] ThrowsOnLeftUnreadable(MethodInfo assign, Type type)222 public static void ThrowsOnLeftUnreadable(MethodInfo assign, Type type) 223 { 224 Func<Expression, Expression, Expression> withAssignment = (Func<Expression, Expression, Expression>)assign.CreateDelegate(typeof(Func<Expression, Expression, Expression>)); 225 226 Type unreadableType = typeof(Unreadable<>).MakeGenericType(type); 227 Expression property = Expression.Property(null, unreadableType.GetProperty("WriteOnly")); 228 AssertExtensions.Throws<ArgumentException>("left", () => withAssignment(property, Expression.Default(type))); 229 } 230 231 [Theory] 232 [MemberData(nameof(AssignmentMethods))] ThrowsOnRightUnreadable(MethodInfo assign, Type type)233 public static void ThrowsOnRightUnreadable(MethodInfo assign, Type type) 234 { 235 Func<Expression, Expression, Expression> withAssignment = (Func<Expression, Expression, Expression>)assign.CreateDelegate(typeof(Func<Expression, Expression, Expression>)); 236 237 Type unreadableType = typeof(Unreadable<>).MakeGenericType(type); 238 Expression property = Expression.Property(null, unreadableType.GetProperty("WriteOnly")); 239 Expression variable = Expression.Variable(type); 240 AssertExtensions.Throws<ArgumentException>("right", () => withAssignment(variable, property)); 241 } 242 243 [Theory] 244 [MemberData(nameof(AssignmentMethodsWithoutTypes))] ThrowIfNoSuchBinaryOperation(MethodInfo assign)245 public void ThrowIfNoSuchBinaryOperation(MethodInfo assign) 246 { 247 Func<Expression, Expression, Expression> withAssignment = (Func<Expression, Expression, Expression>)assign.CreateDelegate(typeof(Func<Expression, Expression, Expression>)); 248 249 ParameterExpression variable = Expression.Variable(typeof(string)); 250 Expression value = Expression.Default(typeof(string)); 251 Assert.Throws<InvalidOperationException>(() => withAssignment(variable, value)); 252 } 253 AssignmentMethods()254 private static IEnumerable<object[]> AssignmentMethods() 255 { 256 MethodInfo[] expressionMethods = typeof(Expression).GetMethods().Where(mi => mi.GetParameters().Length == 2).ToArray(); 257 foreach (Tuple<string, string> names in AssignAndEquivalentMethodNames(true)) 258 yield return new object[] { expressionMethods.First(mi => mi.Name == names.Item2), typeof(int) }; 259 foreach (Tuple<string, string> names in AssignAndEquivalentMethodNames(false)) 260 yield return new object[] { expressionMethods.First(mi => mi.Name == names.Item2), typeof(double) }; 261 } 262 AssignmentMethodsWithoutTypes()263 private static IEnumerable<object[]> AssignmentMethodsWithoutTypes() 264 { 265 MethodInfo[] expressionMethods = typeof(Expression).GetMethods().Where(mi => mi.GetParameters().Length == 2).ToArray(); 266 return AssignAndEquivalentMethodNames(true).Concat(AssignAndEquivalentMethodNames(false)) 267 .Select(i => i.Item2) 268 .Distinct() 269 .Select(i => new object[] { expressionMethods.First(mi => mi.Name == i) }); 270 } 271 AssignAndEquivalentMethods()272 private static IEnumerable<object[]> AssignAndEquivalentMethods() 273 { 274 MethodInfo[] expressionMethods = typeof(Expression).GetMethods().Where(mi => mi.GetParameters().Length == 2).ToArray(); 275 foreach (Tuple<string, string> names in AssignAndEquivalentMethodNames(true)) 276 yield return new object[] { 277 expressionMethods.First(mi => mi.Name == names.Item1), 278 expressionMethods.First(mi => mi.Name == names.Item2), 279 typeof(int) 280 }; 281 foreach (Tuple<string, string> names in AssignAndEquivalentMethodNames(false)) 282 yield return new object[] { 283 expressionMethods.First(mi => mi.Name == names.Item1), 284 expressionMethods.First(mi => mi.Name == names.Item2), 285 typeof(double) 286 }; 287 } 288 AssignAndEquivalentMethodNames(bool integral)289 private static IEnumerable<Tuple<string, string>> AssignAndEquivalentMethodNames(bool integral) 290 { 291 yield return Tuple.Create("Add", "AddAssign"); 292 yield return Tuple.Create("AddChecked", "AddAssignChecked"); 293 yield return Tuple.Create("Divide", "DivideAssign"); 294 yield return Tuple.Create("Modulo", "ModuloAssign"); 295 yield return Tuple.Create("Multiply", "MultiplyAssign"); 296 yield return Tuple.Create("MultiplyChecked", "MultiplyAssignChecked"); 297 yield return Tuple.Create("Subtract", "SubtractAssign"); 298 yield return Tuple.Create("SubtractChecked", "SubtractAssignChecked"); 299 if (integral) 300 { 301 yield return Tuple.Create("And", "AndAssign"); 302 yield return Tuple.Create("ExclusiveOr", "ExclusiveOrAssign"); 303 yield return Tuple.Create("LeftShift", "LeftShiftAssign"); 304 yield return Tuple.Create("Or", "OrAssign"); 305 yield return Tuple.Create("RightShift", "RightShiftAssign"); 306 } 307 else 308 yield return Tuple.Create("Power", "PowerAssign"); 309 } 310 311 [Theory] 312 [MemberData(nameof(ToStringData))] ToStringTest(ExpressionType kind, string symbol, Type type)313 public static void ToStringTest(ExpressionType kind, string symbol, Type type) 314 { 315 BinaryExpression e = Expression.MakeBinary(kind, Expression.Parameter(type, "a"), Expression.Parameter(type, "b")); 316 Assert.Equal($"(a {symbol} b)", e.ToString()); 317 } 318 ToStringData()319 private static IEnumerable<object[]> ToStringData() 320 { 321 return ToStringDataImpl().Select(t => new object[] { t.Item1, t.Item2, t.Item3 }); 322 } 323 ToStringDataImpl()324 private static IEnumerable<Tuple<ExpressionType, string, Type>> ToStringDataImpl() 325 { 326 yield return Tuple.Create(ExpressionType.AddAssign, "+=", typeof(int)); 327 yield return Tuple.Create(ExpressionType.AddAssignChecked, "+=", typeof(int)); 328 yield return Tuple.Create(ExpressionType.SubtractAssign, "-=", typeof(int)); 329 yield return Tuple.Create(ExpressionType.SubtractAssignChecked, "-=", typeof(int)); 330 yield return Tuple.Create(ExpressionType.MultiplyAssign, "*=", typeof(int)); 331 yield return Tuple.Create(ExpressionType.MultiplyAssignChecked, "*=", typeof(int)); 332 yield return Tuple.Create(ExpressionType.DivideAssign, "/=", typeof(int)); 333 yield return Tuple.Create(ExpressionType.ModuloAssign, "%=", typeof(int)); 334 yield return Tuple.Create(ExpressionType.PowerAssign, "**=", typeof(double)); 335 yield return Tuple.Create(ExpressionType.LeftShiftAssign, "<<=", typeof(int)); 336 yield return Tuple.Create(ExpressionType.RightShiftAssign, ">>=", typeof(int)); 337 yield return Tuple.Create(ExpressionType.AndAssign, "&=", typeof(int)); 338 yield return Tuple.Create(ExpressionType.AndAssign, "&&=", typeof(bool)); 339 yield return Tuple.Create(ExpressionType.OrAssign, "|=", typeof(int)); 340 yield return Tuple.Create(ExpressionType.OrAssign, "||=", typeof(bool)); 341 yield return Tuple.Create(ExpressionType.ExclusiveOrAssign, "^=", typeof(int)); 342 yield return Tuple.Create(ExpressionType.ExclusiveOrAssign, "^=", typeof(bool)); 343 } 344 345 private static IEnumerable<ExpressionType> AssignExpressionTypes 346 { 347 get 348 { 349 yield return ExpressionType.AddAssign; 350 yield return ExpressionType.SubtractAssign; 351 yield return ExpressionType.MultiplyAssign; 352 yield return ExpressionType.AddAssignChecked; 353 yield return ExpressionType.SubtractAssignChecked; 354 yield return ExpressionType.MultiplyAssignChecked; 355 yield return ExpressionType.DivideAssign; 356 yield return ExpressionType.ModuloAssign; 357 yield return ExpressionType.PowerAssign; 358 yield return ExpressionType.AndAssign; 359 yield return ExpressionType.OrAssign; 360 yield return ExpressionType.RightShiftAssign; 361 yield return ExpressionType.LeftShiftAssign; 362 yield return ExpressionType.ExclusiveOrAssign; 363 } 364 } 365 366 private static IEnumerable<Func<Expression, Expression, MethodInfo, BinaryExpression>> AssignExpressionMethodInfoUsingFactories 367 { 368 get 369 { 370 yield return Expression.AddAssign; 371 yield return Expression.SubtractAssign; 372 yield return Expression.MultiplyAssign; 373 yield return Expression.AddAssignChecked; 374 yield return Expression.SubtractAssignChecked; 375 yield return Expression.MultiplyAssignChecked; 376 yield return Expression.DivideAssign; 377 yield return Expression.ModuloAssign; 378 yield return Expression.PowerAssign; 379 yield return Expression.AndAssign; 380 yield return Expression.OrAssign; 381 yield return Expression.RightShiftAssign; 382 yield return Expression.LeftShiftAssign; 383 yield return Expression.ExclusiveOrAssign; 384 } 385 } 386 387 public static IEnumerable<object[]> AssignExpressionTypesArguments 388 => AssignExpressionTypes.Select(t => new object[] {t}); 389 390 public static IEnumerable<object[]> AssignExpressionMethodInfoUsingFactoriesArguments = 391 AssignExpressionMethodInfoUsingFactories.Select(f => new object[] {f}); 392 393 private static IEnumerable<LambdaExpression> NonUnaryLambdas 394 { 395 get 396 { 397 yield return Expression.Lambda<Action>(Expression.Empty()); 398 Expression<Func<int, int, int>> exp = (x, y) => x + y; 399 yield return exp; 400 } 401 } 402 403 public static IEnumerable<object[]> AssignExpressionTypesAndNonUnaryLambdas => 404 AssignExpressionTypes.SelectMany(t => NonUnaryLambdas, (t, l) => new object[] {t, l}); 405 406 private static IEnumerable<LambdaExpression> NonIntegerReturnUnaryIntegerLambdas 407 { 408 get 409 { 410 ParameterExpression param = Expression.Parameter(typeof(int)); 411 yield return Expression.Lambda<Action<int>>(Expression.Empty(), param); 412 Expression<Func<int, long>> convL = x => x; 413 yield return convL; 414 Expression<Func<int, string>> toString = x => x.ToString(); 415 yield return toString; 416 } 417 } 418 419 public static IEnumerable<object[]> AssignExpressionTypesAndNonIntegerReturnUnaryIntegerLambdas 420 => AssignExpressionTypes.SelectMany(t => NonIntegerReturnUnaryIntegerLambdas, (t, l) => new object[] {t, l}); 421 422 private static IEnumerable<LambdaExpression> NonIntegerTakingUnaryIntegerReturningLambda 423 { 424 get 425 { 426 Expression<Func<long, int>> fromL = x => (int)x; 427 yield return fromL; 428 Expression<Func<string, int>> fromS = x => x.Length; 429 yield return fromS; 430 } 431 } 432 433 public static IEnumerable<object[]> AssignExpressionTypesAndNonIntegerTakingUnaryIntegerReturningLambda 434 => 435 AssignExpressionTypes.SelectMany( 436 t => NonIntegerTakingUnaryIntegerReturningLambda, (t, l) => new object[] {t, l}); 437 438 [Theory, MemberData(nameof(AssignExpressionTypesArguments))] CannotHaveConversionOnAssignWithoutMethod(ExpressionType type)439 public void CannotHaveConversionOnAssignWithoutMethod(ExpressionType type) 440 { 441 var lhs = Expression.Variable(typeof(int)); 442 var rhs = Expression.Constant(0); 443 Expression<Func<int, int>> identity = x => x; 444 Assert.Throws<InvalidOperationException>(() => Expression.MakeBinary(type, lhs, rhs, false, null, identity)); 445 Assert.Throws<InvalidOperationException>(() => Expression.MakeBinary(type, lhs, rhs, true, null, identity)); 446 } 447 FiftyNinthBear(int x, int y)448 public static int FiftyNinthBear(int x, int y) 449 { 450 // Ensure numbers add up to 40. Then ignore that and return 59. 451 if (x + y != 40) throw new ArgumentException(); 452 return 59; 453 } 454 455 [Theory, PerCompilationType(nameof(AssignExpressionTypesArguments))] ConvertAssignment(ExpressionType type, bool useInterpreter)456 public void ConvertAssignment(ExpressionType type, bool useInterpreter) 457 { 458 var lhs = Expression.Parameter(typeof(int)); 459 var rhs = Expression.Constant(25); 460 Expression<Func<int, int>> doubleIt = x => 2 * x; 461 var lambda = Expression.Lambda<Func<int, int>>( 462 Expression.MakeBinary(type, lhs, rhs, false, GetType().GetMethod(nameof(FiftyNinthBear)), doubleIt), 463 lhs 464 ); 465 var func = lambda.Compile(useInterpreter); 466 Assert.Equal(118, func(15)); 467 } 468 469 [Theory, MemberData(nameof(AssignExpressionTypesAndNonUnaryLambdas))] ConversionMustBeUnary(ExpressionType type, LambdaExpression conversion)470 public void ConversionMustBeUnary(ExpressionType type, LambdaExpression conversion) 471 { 472 var lhs = Expression.Parameter(typeof(int)); 473 var rhs = Expression.Constant(25); 474 MethodInfo meth = GetType().GetMethod(nameof(FiftyNinthBear)); 475 AssertExtensions.Throws<ArgumentException>( 476 "conversion", () => Expression.MakeBinary(type, lhs, rhs, false, meth, conversion)); 477 } 478 479 [Theory, MemberData(nameof(AssignExpressionTypesAndNonIntegerReturnUnaryIntegerLambdas))] ConversionMustConvertToLHSType(ExpressionType type, LambdaExpression conversion)480 public void ConversionMustConvertToLHSType(ExpressionType type, LambdaExpression conversion) 481 { 482 var lhs = Expression.Parameter(typeof(int)); 483 var rhs = Expression.Constant(25); 484 MethodInfo meth = GetType().GetMethod(nameof(FiftyNinthBear)); 485 Assert.Throws<InvalidOperationException>(() => Expression.MakeBinary(type, lhs, rhs, false, meth, conversion)); 486 } 487 488 [Theory, MemberData(nameof(AssignExpressionTypesAndNonIntegerTakingUnaryIntegerReturningLambda))] ConversionMustConvertFromRHSType(ExpressionType type, LambdaExpression conversion)489 public void ConversionMustConvertFromRHSType(ExpressionType type, LambdaExpression conversion) 490 { 491 var lhs = Expression.Parameter(typeof(int)); 492 var rhs = Expression.Constant(25); 493 MethodInfo meth = GetType().GetMethod(nameof(FiftyNinthBear)); 494 Assert.Throws<InvalidOperationException>(() => Expression.MakeBinary(type, lhs, rhs, false, meth, conversion)); 495 } 496 497 private class AddsToSomethingElse : IEquatable<AddsToSomethingElse> 498 { 499 public int Value { get; } 500 AddsToSomethingElse(int value)501 public AddsToSomethingElse(int value) 502 { 503 Value = value; 504 } 505 operator +(AddsToSomethingElse x, AddsToSomethingElse y)506 public static int operator +(AddsToSomethingElse x, AddsToSomethingElse y) => x.Value + y.Value; 507 508 public bool Equals(AddsToSomethingElse other) => Value == other?.Value; 509 510 public override bool Equals(object obj) => Equals(obj as AddsToSomethingElse); 511 GetHashCode()512 public override int GetHashCode() => Value; 513 } 514 StringAddition(int x, int y)515 private static string StringAddition(int x, int y) => (x + y).ToString(); 516 517 [Fact] CannotAssignOpIfOpReturnNotAssignable()518 public void CannotAssignOpIfOpReturnNotAssignable() 519 { 520 var lhs = Expression.Parameter(typeof(AddsToSomethingElse)); 521 var rhs = Expression.Constant(new AddsToSomethingElse(3)); 522 AssertExtensions.Throws<ArgumentException>(null, () => Expression.AddAssign(lhs, rhs)); 523 } 524 525 [Theory, ClassData(typeof(CompilationTypes))] CanAssignOpIfOpReturnNotAssignableButConversionFixes(bool useInterpreter)526 public void CanAssignOpIfOpReturnNotAssignableButConversionFixes(bool useInterpreter) 527 { 528 var lhs = Expression.Parameter(typeof(AddsToSomethingElse)); 529 var rhs = Expression.Constant(new AddsToSomethingElse(3)); 530 Expression<Func<int, AddsToSomethingElse>> conversion = x => new AddsToSomethingElse(x); 531 var exp = Expression.Lambda<Func<AddsToSomethingElse, AddsToSomethingElse>>( 532 Expression.AddAssign(lhs, rhs, null, conversion), 533 lhs 534 ); 535 var func = exp.Compile(useInterpreter); 536 Assert.Equal(new AddsToSomethingElse(10), func(new AddsToSomethingElse(7))); 537 } 538 539 [Theory, PerCompilationType(nameof(AssignExpressionTypesArguments))] ConvertOpAssignToMember(ExpressionType type, bool useInterpreter)540 public void ConvertOpAssignToMember(ExpressionType type, bool useInterpreter) 541 { 542 Box<int> box = new Box<int>(25); 543 Expression<Func<int, int>> doubleIt = x => x * 2; 544 var exp = Expression.Lambda<Func<int>>( 545 Expression.MakeBinary( 546 type, 547 Expression.Property(Expression.Constant(box), "Value"), 548 Expression.Constant(15), 549 false, 550 GetType().GetMethod(nameof(FiftyNinthBear)), 551 doubleIt 552 ) 553 ); 554 var act = exp.Compile(useInterpreter); 555 Assert.Equal(118, act()); 556 Assert.Equal(118, box.Value); 557 } 558 559 [Theory, PerCompilationType(nameof(AssignExpressionTypesArguments))] ConvertOpAssignToArrayIndex(ExpressionType type, bool useInterpreter)560 public void ConvertOpAssignToArrayIndex(ExpressionType type, bool useInterpreter) 561 { 562 int[] array = {0, 0, 25, 0}; 563 Expression<Func<int, int>> doubleIt = x => x * 2; 564 var exp = Expression.Lambda<Func<int>>( 565 Expression.MakeBinary( 566 type, 567 Expression.ArrayAccess(Expression.Constant(array), Expression.Constant(2)), 568 Expression.Constant(15), 569 false, 570 GetType().GetMethod(nameof(FiftyNinthBear)), 571 doubleIt 572 ) 573 ); 574 var act = exp.Compile(useInterpreter); 575 Assert.Equal(118, act()); 576 Assert.Equal(118, array[2]); 577 } 578 ByRefInts(ref int x, int y)579 private delegate int ByRefInts(ref int x, int y); 580 BothByRefInts(ref int x, ref int y)581 private delegate int BothByRefInts(ref int x, ref int y); 582 583 [Theory, PerCompilationType(nameof(AssignExpressionMethodInfoUsingFactoriesArguments))] MethodNoConvertOpWriteByRefParameter(Func<Expression, Expression, MethodInfo, BinaryExpression> factory, bool useInterpreter)584 public void MethodNoConvertOpWriteByRefParameter(Func<Expression, Expression, MethodInfo, BinaryExpression> factory, bool useInterpreter) 585 { 586 var pX = Expression.Parameter(typeof(int).MakeByRefType()); 587 var pY = Expression.Parameter(typeof(int)); 588 var exp = Expression.Lambda<ByRefInts>(factory(pX, pY, GetType().GetMethod(nameof(FiftyNinthBear))), pX, pY); 589 var del = exp.Compile(useInterpreter); 590 int arg = 5; 591 Assert.Equal(59, del(ref arg, 35)); 592 Assert.Equal(59, arg); 593 } 594 ByRefSomeElse(ref AddsToSomethingElse x, AddsToSomethingElse y)595 private delegate AddsToSomethingElse ByRefSomeElse(ref AddsToSomethingElse x, AddsToSomethingElse y); 596 597 [Theory, ClassData(typeof(CompilationTypes))] ConvertOpWriteByRefParameterOverloadedOperator(bool useInterpreter)598 public void ConvertOpWriteByRefParameterOverloadedOperator(bool useInterpreter) 599 { 600 var pX = Expression.Parameter(typeof(AddsToSomethingElse).MakeByRefType()); 601 var pY = Expression.Parameter(typeof(AddsToSomethingElse)); 602 Expression<Func<int, AddsToSomethingElse>> conv = x => new AddsToSomethingElse(x); 603 var exp = Expression.Lambda<ByRefSomeElse>(Expression.AddAssign(pX, pY, null, conv), pX, pY); 604 var del = exp.Compile(useInterpreter); 605 AddsToSomethingElse arg = new AddsToSomethingElse(5); 606 AddsToSomethingElse result = del(ref arg, new AddsToSomethingElse(35)); 607 Assert.Equal(result, arg); 608 } 609 } 610 } 611