PageRenderTime 59ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/IronPython_Main/Runtime/Tests/AstTest/Scenarios.Logical.cs

#
C# | 1051 lines | 856 code | 131 blank | 64 comment | 69 complexity | fb6e44a5b425ef494b50dc4170e93c33 MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception, CPL-1.0, CC-BY-SA-3.0, BSD-3-Clause, ISC, AGPL-3.0, LGPL-2.1, Apache-2.0
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. #if !SILVERLIGHT3
  16. #if !CLR2
  17. using System.Linq.Expressions;
  18. #else
  19. using Microsoft.Scripting.Ast;
  20. using Microsoft.Scripting.Utils;
  21. #endif
  22. using System;
  23. using System.Collections;
  24. using System.Collections.Generic;
  25. using System.Collections.ObjectModel;
  26. using System.Reflection.Emit;
  27. using System.Runtime.CompilerServices;
  28. using EU = ETUtils.ExpressionUtils;
  29. namespace AstTest {
  30. // Tests of the logical operator optimizations (== != && || ! typeis)
  31. // (We want to generate good IL for the common cases but not regress the
  32. // general behavior)
  33. public static partial class Scenarios {
  34. #region Slow_OptimizedLogical
  35. // Marked as slow so we get collectible delegates
  36. public static void Slow_OptimizedLogical() {
  37. var rng = new Random(0xCAFE);
  38. int n = 0x1000;
  39. for (int i = 0; i < n; i++) {
  40. var testExpr = MakeLogicalExpression(rng, 8);
  41. bool baseline = EvalLogicalExpression(testExpr);
  42. var func = Expression.Lambda<Func<bool>>(testExpr).Compile();
  43. bool result = func();
  44. EU.Equal(baseline, result);
  45. }
  46. }
  47. public static bool OptimizedLogical_GetTrue() { return true; }
  48. public static bool OptimizedLogical_GetFalse() { return false; }
  49. // Build an arbitrary logical expression:
  50. // Leaf nodes are:
  51. // Constant(boolValue)
  52. // Method returning boolValue
  53. // Equal(boolValue, null comparison)
  54. // NotEqual(boolValue, null comparison)
  55. // Non-leaf nodes are:
  56. // AndAlso
  57. // OrElse
  58. // Conditional
  59. private static readonly Expression _OptimizedLogical_TrueConstant = Expression.Constant(true);
  60. private static readonly Expression _OptimizedLogical_FalseConstant = Expression.Constant(false);
  61. private static readonly Expression _OptimizedLogical_TrueMethod = Expression.Call(typeof(Scenarios).GetMethod("OptimizedLogical_GetTrue"));
  62. private static readonly Expression _OptimizedLogical_FalseMethod = Expression.Call(typeof(Scenarios).GetMethod("OptimizedLogical_GetFalse"));
  63. private static readonly Expression _OptimizedLogical_TrueEqual = Expression.Equal(Expression.Constant(null), Expression.Constant(null));
  64. private static readonly Expression _OptimizedLogical_FalseEqual = Expression.Equal(Expression.Constant(new object()), Expression.Constant(null));
  65. private static readonly Expression _OptimizedLogical_TrueNotEqual = Expression.NotEqual(Expression.Constant(new object()), Expression.Constant(null));
  66. private static readonly Expression _OptimizedLogical_FalseNotEqual = Expression.NotEqual(Expression.Constant(null), Expression.Constant(null));
  67. private static Expression MakeLogicalExpression(Random rng, int maxDepth) {
  68. if (rng.Next(4) == 0 || maxDepth == 1) {
  69. switch (rng.Next(8)) {
  70. case 0: return _OptimizedLogical_TrueConstant;
  71. case 1: return _OptimizedLogical_FalseConstant;
  72. case 2: return _OptimizedLogical_TrueMethod;
  73. case 3: return _OptimizedLogical_FalseMethod;
  74. case 4: return _OptimizedLogical_TrueEqual;
  75. case 5: return _OptimizedLogical_FalseEqual;
  76. case 6: return _OptimizedLogical_TrueNotEqual;
  77. case 7: return _OptimizedLogical_FalseNotEqual;
  78. }
  79. } else {
  80. maxDepth--;
  81. switch (rng.Next(4)) {
  82. case 0:
  83. return Expression.AndAlso(
  84. MakeLogicalExpression(rng, maxDepth),
  85. MakeLogicalExpression(rng, maxDepth)
  86. );
  87. case 1:
  88. return Expression.OrElse(
  89. MakeLogicalExpression(rng, maxDepth),
  90. MakeLogicalExpression(rng, maxDepth)
  91. );
  92. case 2:
  93. return Expression.Condition(
  94. MakeLogicalExpression(rng, maxDepth),
  95. MakeLogicalExpression(rng, maxDepth),
  96. MakeLogicalExpression(rng, maxDepth)
  97. );
  98. case 3:
  99. return Expression.Not(
  100. MakeLogicalExpression(rng, maxDepth)
  101. );
  102. }
  103. }
  104. throw new Exception("unreachable");
  105. }
  106. private static bool EvalLogicalExpression(Expression node) {
  107. switch (node.NodeType) {
  108. case ExpressionType.Constant:
  109. return node == _OptimizedLogical_TrueConstant;
  110. case ExpressionType.Call:
  111. return node == _OptimizedLogical_TrueMethod;
  112. case ExpressionType.Equal:
  113. return node == _OptimizedLogical_TrueEqual;
  114. case ExpressionType.NotEqual:
  115. return node == _OptimizedLogical_TrueNotEqual;
  116. case ExpressionType.AndAlso:
  117. var andAlso = (BinaryExpression)node;
  118. return EvalLogicalExpression(andAlso.Left) && EvalLogicalExpression(andAlso.Right);
  119. case ExpressionType.OrElse:
  120. var orElse = (BinaryExpression)node;
  121. return EvalLogicalExpression(orElse.Left) || EvalLogicalExpression(orElse.Right);
  122. case ExpressionType.Conditional:
  123. var cond = (ConditionalExpression)node;
  124. return EvalLogicalExpression(cond.Test) ? EvalLogicalExpression(cond.IfTrue) : EvalLogicalExpression(cond.IfFalse);
  125. case ExpressionType.Not:
  126. var not = (UnaryExpression)node;
  127. return !EvalLogicalExpression(not.Operand);
  128. default: throw new Exception("unreachable");
  129. }
  130. }
  131. #endregion
  132. #region Positive_OptimizedTypeIs
  133. public enum EnumInt16 : short {
  134. A, B, C
  135. }
  136. public enum EnumUInt16 : ushort {
  137. A, B, C
  138. }
  139. public enum EnumInt32 : int {
  140. A, B, C
  141. }
  142. public enum EnumUInt32 : uint {
  143. A, B, C
  144. }
  145. //ICloneable is inaccessible in Silverlight and the ComputeTypeIs call below will cause us to iterate over it and fail.
  146. #if !SILVERLIGHT
  147. // Compare TypeIs result for a whole bunch of types, it should be
  148. // equivalent to isinst
  149. //
  150. // Don't run this with SaveAssemblies because it compiles very many ETs
  151. // If run with SaveAssemblies on, it will run out of memory because
  152. // TypeBuilder's aren't collectible.
  153. public static void Positive_OptimizedTypeIs(EU.IValidator V) {
  154. var types = new List<Type> {
  155. typeof(Byte), typeof(SByte), typeof(Boolean),
  156. typeof(Int16), typeof(UInt16), typeof(Char),
  157. typeof(Int32), typeof(UInt32),
  158. typeof(Int64), typeof(UInt64),
  159. typeof(Single), typeof(Double), typeof(Decimal),
  160. typeof(DateTime),
  161. typeof(String),
  162. typeof(Object),
  163. typeof(DBNull),
  164. typeof(TimeSpan), // struct
  165. typeof(EnumInt16), typeof(EnumUInt16), typeof(EnumInt32), typeof(EnumUInt32),
  166. // special types
  167. typeof(ValueType), typeof(Enum), typeof(Array),
  168. typeof(IEnumerable), typeof(IComparable), typeof(IList),
  169. };
  170. var objs = new List<object>();
  171. objs.Add(null);
  172. foreach (Type t in types.ToArray()) {
  173. types.Add(t.MakeArrayType());
  174. if (t.IsValueType) {
  175. types.Add(typeof(Nullable<>).MakeGenericType(t));
  176. }
  177. types.Add(typeof(IEnumerable<>).MakeGenericType(t));
  178. if (!t.IsInterface && !t.IsAbstract) {
  179. object obj;
  180. if (t == typeof(string)) {
  181. obj = "Hello";
  182. } else if (t == typeof(DBNull)) {
  183. obj = DBNull.Value;
  184. } else {
  185. obj = Activator.CreateInstance(t);
  186. }
  187. objs.Add(obj);
  188. objs.Add(Array.CreateInstance(t, 0));
  189. }
  190. }
  191. foreach (var obj in objs) {
  192. //Console.Write('.');
  193. foreach (var type in types) {
  194. bool expected = ComputeIsinst(obj, type);
  195. Type objType = obj != null ? obj.GetType() : type;
  196. foreach (var manifestType in GetManifestTypes(objType)) {
  197. if (obj == null && manifestType.IsValueType && Nullable.GetUnderlyingType(manifestType) == null) {
  198. // Can't cast null to a value type
  199. continue;
  200. }
  201. bool result = ComputeTypeIs(obj, type, manifestType);
  202. if (expected != result) {
  203. throw new InvalidOperationException(
  204. string.Format(
  205. "Test failed: expected '{0}' (type: {1}) TypeIs {2} to equal {3}, but got {4} when manifest type is {5}",
  206. obj ?? "<null>", objType, type, expected, result, manifestType
  207. )
  208. );
  209. }
  210. }
  211. }
  212. }
  213. }
  214. #endif
  215. private static bool ComputeTypeIs(object obj, Type type, Type manifestType) {
  216. var arg = Expression.Parameter(typeof(object), "arg");
  217. var test = Expression.Lambda<Func<object, bool>>(
  218. Expression.TypeIs(Expression.Convert(arg, manifestType), type),
  219. arg
  220. ).Compile();
  221. return test(obj);
  222. }
  223. // Gets the type that this object appears to be to the compiler
  224. // This is not the same as the run time type.
  225. private static IEnumerable<Type> GetManifestTypes(Type type) {
  226. for (Type t = type; t != null; t = t.BaseType) {
  227. yield return t;
  228. }
  229. if (type.IsValueType && Nullable.GetUnderlyingType(type) == null) {
  230. // can be Nullable<T>
  231. yield return typeof(Nullable<>).MakeGenericType(type);
  232. }
  233. foreach (Type i in type.GetInterfaces()) {
  234. yield return i;
  235. // only return one interface to make the test faster
  236. yield break;
  237. }
  238. }
  239. private static bool ComputeIsinst(object obj, Type type) {
  240. var dm = new DynamicMethod("test", typeof(bool), new[] { typeof(object) });
  241. var il = dm.GetILGenerator();
  242. il.Emit(OpCodes.Ldarg_0);
  243. il.Emit(OpCodes.Isinst, type);
  244. il.Emit(OpCodes.Ldnull);
  245. il.Emit(OpCodes.Cgt_Un);
  246. il.Emit(OpCodes.Ret);
  247. var test = (Func<object, bool>)dm.CreateDelegate(typeof(Func<object, bool>));
  248. return test(obj);
  249. }
  250. #endregion
  251. #region Positive_TypeEqual
  252. private static bool ComputeTypeEqual(object obj, Type type, Type manifestType) {
  253. var arg = Expression.Parameter(typeof(object), "arg");
  254. var temp = Expression.Parameter(typeof(int), "temp");
  255. var result1 = Expression.Parameter(typeof(bool), "result1");
  256. var result2 = Expression.Parameter(typeof(bool), "result2");
  257. var sideEffectExpr = Expression.Block(
  258. Expression.Assign(temp, Expression.Constant(42)),
  259. arg
  260. );
  261. var expr1 = Expression.Assign(
  262. result1,
  263. Expression.TypeEqual(Expression.Convert(arg, manifestType), type)
  264. );
  265. var expr2 = Expression.Assign(
  266. result2,
  267. Expression.TypeEqual(Expression.Convert(sideEffectExpr, manifestType), type)
  268. );
  269. var block = Expression.Block(
  270. new ParameterExpression[] { temp, result1, result2 },
  271. expr1,
  272. expr2,
  273. Expression.Condition(
  274. Expression.Equal(result1, result2),
  275. Expression.Empty(),
  276. Expression.Throw(Expression.Constant(new Exception("different results for TypeEquals")))
  277. ),
  278. Expression.Condition(
  279. Expression.Equal(temp, Expression.Constant(42)),
  280. Expression.Empty(),
  281. Expression.Throw(Expression.Constant(new Exception("no expected sideeffects")))
  282. ),
  283. result1
  284. );
  285. var test = Expression.Lambda<Func<object, bool>>(
  286. block,
  287. arg
  288. );
  289. return test.Compile()(obj);
  290. }
  291. //ICloneable is inaccessible in Silverlight and the ComputeTypeIs call below will cause us to iterate over it and fail.
  292. #if !SILVERLIGHT
  293. public static void Positive_TypeEqual(EU.IValidator V) {
  294. var types = new List<Type> {
  295. typeof(Byte),
  296. typeof(SByte),
  297. typeof(String),
  298. typeof(Object),
  299. typeof(DBNull),
  300. typeof(TimeSpan), // struct
  301. typeof(EnumInt16), typeof(EnumUInt16),
  302. // special types
  303. typeof(ValueType), typeof(Enum), typeof(Array),
  304. typeof(IEnumerable), typeof(IComparable), typeof(IList)
  305. };
  306. var objs = new List<object>();
  307. objs.Add(null);
  308. foreach (Type t in types.ToArray()) {
  309. types.Add(t.MakeArrayType());
  310. Type nullable = null;
  311. if (t.IsValueType) {
  312. types.Add(nullable = typeof(Nullable<>).MakeGenericType(t));
  313. }
  314. types.Add(typeof(IEnumerable<>).MakeGenericType(t));
  315. if (!t.IsInterface && !t.IsAbstract) {
  316. object obj;
  317. if (t == typeof(string)) {
  318. obj = "Hello";
  319. } else if (t == typeof(DBNull)) {
  320. obj = DBNull.Value;
  321. } else {
  322. obj = Activator.CreateInstance(t);
  323. }
  324. objs.Add(obj);
  325. objs.Add(Array.CreateInstance(t, 0));
  326. }
  327. }
  328. foreach (var obj in objs) {
  329. //Console.Write('.');
  330. foreach (var type in types) {
  331. Type objType = obj != null ? obj.GetType() : type;
  332. foreach (var manifestType in GetManifestTypes(objType)) {
  333. if (obj == null && manifestType.IsValueType && Nullable.GetUnderlyingType(manifestType) == null) {
  334. // Can't cast null to a value type
  335. continue;
  336. }
  337. bool result = ComputeTypeEqual(obj, type, manifestType);
  338. bool expected = TypeEqual(obj, type, manifestType);
  339. if (expected != result) {
  340. throw new InvalidOperationException(
  341. string.Format(
  342. "Test failed: expected '{0}' (type: {1}) TypeEquals {2} to equal {3}, but got {4} when manifest type is {5}",
  343. obj ?? "<null>", objType, type, expected, result, manifestType
  344. )
  345. );
  346. }
  347. }
  348. }
  349. }
  350. }
  351. #endif
  352. private static bool TypeEqual(object obj, Type type, Type manifestType) {
  353. return obj != null && obj.GetType() == GetNonNullable(type);
  354. }
  355. private static Type GetNonNullable(Type t) {
  356. return Nullable.GetUnderlyingType(t) ?? t;
  357. }
  358. private static bool IsNullable(Type t) {
  359. return Nullable.GetUnderlyingType(t) != null;
  360. }
  361. public static Expression Positive_TypeIsVoid(EU.IValidator V) {
  362. _VoidMethodCalled = 0;
  363. var e = Expression.Lambda<Func<bool>>(
  364. Expression.TypeIs(Expression.Call(typeof(Scenarios).GetMethod("VoidMethod")), typeof(void))
  365. );
  366. V.Validate(e, f =>
  367. {
  368. bool voidResult = f();
  369. EU.Equal(voidResult, false);
  370. EU.Equal(_VoidMethodCalled, 1);
  371. });
  372. return e;
  373. }
  374. static int _VoidMethodCalled = 0;
  375. public static void VoidMethod() { _VoidMethodCalled++; }
  376. public static Expression Positive_TypeIsNull(EU.IValidator V) {
  377. // Constant null
  378. var e = Expression.Lambda<Func<bool>>(
  379. Expression.TypeIs(Expression.Constant(null), typeof(object))
  380. );
  381. V.Validate(e, f =>
  382. {
  383. EU.Equal(f(), false);
  384. });
  385. // Constant not-null
  386. e = Expression.Lambda<Func<bool>>(
  387. Expression.TypeIs(Expression.Constant(new object()), typeof(object))
  388. );
  389. V.Validate(e, f =>
  390. {
  391. EU.Equal(f(), true);
  392. });
  393. // Argument null/non-null
  394. var x = Expression.Parameter(typeof(object), null);
  395. var e2 = Expression.Lambda<Func<object, bool>>(Expression.TypeIs(x, typeof(object)), x);
  396. V.Validate(e2, f2 =>
  397. {
  398. EU.Equal(f2(null), false);
  399. EU.Equal(f2(123), true);
  400. EU.Equal(f2(new object()), true);
  401. });
  402. return e;
  403. }
  404. public static Expression Positive_TypeIsNullable(EU.IValidator V) {
  405. // Constant null
  406. var e = Expression.Lambda<Func<bool>>(
  407. Expression.TypeIs(Expression.Constant(null), typeof(int?))
  408. );
  409. V.Validate(e, f =>
  410. {
  411. EU.Equal(f(), false);
  412. });
  413. // default(T), equivalent to null
  414. e = Expression.Lambda<Func<bool>>(
  415. Expression.TypeIs(Expression.Constant(default(int?), typeof(int?)), typeof(int?))
  416. );
  417. V.Validate(e, f =>
  418. {
  419. EU.Equal(f(), false);
  420. });
  421. // Constant not-null
  422. e = Expression.Lambda<Func<bool>>(
  423. Expression.TypeIs(Expression.Constant(123, typeof(int?)), typeof(int?))
  424. );
  425. V.Validate(e, f =>
  426. {
  427. EU.Equal(f(), true);
  428. });
  429. e = Expression.Lambda<Func<bool>>(
  430. Expression.TypeIs(Expression.Constant(123, typeof(int?)), typeof(int?))
  431. );
  432. V.Validate(e, f =>
  433. {
  434. EU.Equal(f(), true);
  435. });
  436. // Argument null/default(T)/non-null
  437. var x = Expression.Parameter(typeof(int?), null);
  438. var e2 = Expression.Lambda<Func<int?, bool>>(Expression.TypeIs(x, typeof(int?)), x);
  439. V.Validate(e2, f2 =>
  440. {
  441. EU.Equal(f2(null), false);
  442. EU.Equal(f2(123), true);
  443. EU.Equal(f2(default(int?)), false);
  444. });
  445. var y = Expression.Parameter(typeof(object), null);
  446. var e3 = Expression.Lambda<Func<object, bool>>(Expression.TypeIs(y, typeof(int?)), y);
  447. V.Validate(e3, f3 =>
  448. {
  449. EU.Equal(f3(null), false);
  450. EU.Equal(f3(123), true);
  451. EU.Equal(f3(default(int?)), false);
  452. EU.Equal(f3(new object()), false);
  453. });
  454. return e;
  455. }
  456. #endregion
  457. public static DateTime TestDateField;
  458. public static int TestIntField = 2;
  459. public static decimal TestDecimalField = decimal.One;
  460. public static decimal VBConvertToDecimal(bool value) {
  461. return value ? decimal.MinusOne : decimal.Zero;
  462. }
  463. public static Expression Positive_ComplexLogical(EU.IValidator V) {
  464. var e = Expression.Lambda<Func<bool>>(
  465. Expression.AndAlso(
  466. Expression.NotEqual(
  467. Expression.Convert(
  468. Expression.TypeIs(
  469. Expression.Convert(Expression.Field(null, typeof(Scenarios), "TestDateField"), typeof(object)),
  470. typeof(double)
  471. ),
  472. typeof(decimal),
  473. typeof(Scenarios).GetMethod("VBConvertToDecimal")
  474. ),
  475. Expression.Add(
  476. Expression.Field(null, typeof(Scenarios), "TestDecimalField"),
  477. Expression.Convert(
  478. Expression.Field(null, typeof(Scenarios), "testIntField"),
  479. typeof(decimal),
  480. typeof(decimal).GetMethod("op_Implicit", new[] { typeof(int) })
  481. ),
  482. typeof(decimal).GetMethod("Add")
  483. ),
  484. true,
  485. typeof(decimal).GetMethod("op_Inequality")
  486. ),
  487. Expression.Constant(true, typeof(bool))
  488. )
  489. );
  490. V.Validate(e, f =>
  491. {
  492. EU.Equal(f(), true);
  493. });
  494. return e;
  495. }
  496. public struct TestShortCircuit {
  497. public readonly bool Value;
  498. public TestShortCircuit(bool val) {
  499. Value = val;
  500. }
  501. public static TestShortCircuit operator |(TestShortCircuit a, TestShortCircuit b) {
  502. return new TestShortCircuit(a.Value | b.Value);
  503. }
  504. public static TestShortCircuit operator &(TestShortCircuit a, TestShortCircuit b) {
  505. return new TestShortCircuit(a.Value & b.Value);
  506. }
  507. public static bool operator true(TestShortCircuit a) {
  508. return a.Value;
  509. }
  510. public static bool operator false(TestShortCircuit a) {
  511. return !a.Value;
  512. }
  513. public override string ToString() {
  514. return Value.ToString();
  515. }
  516. }
  517. // .NET semantics for userdefined short circuiting operators
  518. public static void Positive_ShortCircuit_Userdefined_AndAlso(EU.IValidator V) {
  519. foreach (bool left in new[] { true, false }) {
  520. foreach (bool right in new[] { true, false }) {
  521. bool evaluatedLeft = false;
  522. bool evaluatedRight = false;
  523. var e = Expression.Lambda<Func<TestShortCircuit>>(
  524. Expression.AndAlso(
  525. Expression.Invoke(
  526. Expression.Constant(
  527. new Func<TestShortCircuit>(() => { evaluatedLeft = true; return new TestShortCircuit(left); })
  528. )
  529. ),
  530. Expression.Invoke(
  531. Expression.Constant(
  532. new Func<TestShortCircuit>(() => { evaluatedRight = true; return new TestShortCircuit(right); })
  533. )
  534. )
  535. )
  536. );
  537. V.Validate(e, f =>
  538. {
  539. var actual = ((TestShortCircuit)f()).Value;
  540. EU.Equal(actual, left & right);
  541. EU.Equal(evaluatedLeft, true);
  542. EU.Equal(evaluatedRight, left);
  543. });
  544. }
  545. }
  546. }
  547. // see comment for AndAlso (above)
  548. public static void Positive_ShortCircuit_Userdefined_OrElse(EU.IValidator V) {
  549. foreach (var left in new[] { true, false }) {
  550. foreach (var right in new[] { true, false }) {
  551. bool evaluatedLeft = false;
  552. bool evaluatedRight = false;
  553. var e = Expression.Lambda<Func<TestShortCircuit>>(
  554. Expression.OrElse(
  555. Expression.Invoke(
  556. Expression.Constant(
  557. new Func<TestShortCircuit>(() => { evaluatedLeft = true; return new TestShortCircuit(left); })
  558. )
  559. ),
  560. Expression.Invoke(
  561. Expression.Constant(
  562. new Func<TestShortCircuit>(() => { evaluatedRight = true; return new TestShortCircuit(right); })
  563. )
  564. )
  565. )
  566. );
  567. V.Validate(e, f =>
  568. {
  569. var actual = ((TestShortCircuit)f()).Value;
  570. EU.Equal(actual, left | right);
  571. EU.Equal(evaluatedLeft, true);
  572. EU.Equal(evaluatedRight, !left);
  573. });
  574. }
  575. }
  576. }
  577. // Expects VB.NET semantics for (bool? AndAlso bool?)
  578. // Essentially, three valued logic where "null" is "unknown"
  579. public static void Positive_ShortCircuit_Lifted_AndAlso(EU.IValidator V) {
  580. foreach (var left in new bool?[] { true, false, null }) {
  581. foreach (var right in new bool?[] { true, false, null }) {
  582. bool evaluatedLeft = false;
  583. bool evaluatedRight = false;
  584. var actual = Expression.Lambda<Func<bool?>>(
  585. Expression.AndAlso(
  586. Expression.Invoke(
  587. Expression.Constant(
  588. new Func<bool?>(() => { evaluatedLeft = true; return left; })
  589. )
  590. ),
  591. Expression.Invoke(
  592. Expression.Constant(
  593. new Func<bool?>(() => { evaluatedRight = true; return right; })
  594. )
  595. )
  596. )
  597. ).Compile()();
  598. EU.Equal(actual, left & right);
  599. EU.Equal(evaluatedLeft, true);
  600. EU.Equal(evaluatedRight, left != false);
  601. }
  602. }
  603. }
  604. // see comment for AndAlso (above)
  605. public static void Positive_ShortCircuit_Lifted_OrElse(EU.IValidator V) {
  606. foreach (var left in new bool?[] { true, false, null }) {
  607. foreach (var right in new bool?[] { true, false, null }) {
  608. bool evaluatedLeft = false;
  609. bool evaluatedRight = false;
  610. var actual = Expression.Lambda<Func<bool?>>(
  611. Expression.OrElse(
  612. Expression.Invoke(
  613. Expression.Constant(
  614. new Func<bool?>(() => { evaluatedLeft = true; return left; })
  615. )
  616. ),
  617. Expression.Invoke(
  618. Expression.Constant(
  619. new Func<bool?>(() => { evaluatedRight = true; return right; })
  620. )
  621. )
  622. )
  623. ).Compile()();
  624. EU.Equal(actual, left | right);
  625. EU.Equal(evaluatedLeft, true);
  626. EU.Equal(evaluatedRight, left != true);
  627. }
  628. }
  629. }
  630. // Expects VB.NET semantics for (Userdefined? AndAlso Userdefined?)
  631. // It is the same as the normal userdefined case, but if either operand
  632. // is null, the result will be null.
  633. public static void Positive_ShortCircuit_LiftedUserdefined_AndAlso(EU.IValidator V) {
  634. var values = new TestShortCircuit?[] { new TestShortCircuit(true), new TestShortCircuit(false), null };
  635. foreach (var left in values) {
  636. foreach (var right in values) {
  637. bool evaluatedLeft = false, evaluatedRight = false;
  638. var e = Expression.Lambda<Func<TestShortCircuit?>>(
  639. Expression.AndAlso(
  640. Expression.Invoke(
  641. Expression.Constant(
  642. new Func<TestShortCircuit?>(() => { evaluatedLeft = true; return left; })
  643. )
  644. ),
  645. Expression.Invoke(
  646. Expression.Constant(
  647. new Func<TestShortCircuit?>(() => { evaluatedRight = true; return right; })
  648. )
  649. )
  650. )
  651. );
  652. V.Validate(e, f =>
  653. {
  654. var actual = f();
  655. bool expectedEvalRight = false;
  656. var expected = AndAlso(
  657. () => { return left; },
  658. () => { expectedEvalRight = true; return right; }
  659. );
  660. EU.Equal(actual, expected);
  661. EU.Equal(evaluatedLeft, true);
  662. EU.Equal(evaluatedRight, expectedEvalRight);
  663. });
  664. }
  665. }
  666. }
  667. // see comment for AndAlso (above)
  668. public static void Positive_ShortCircuit_LiftedUserdefined_OrElse(EU.IValidator V) {
  669. var values = new TestShortCircuit?[] { new TestShortCircuit(true), new TestShortCircuit(false), null };
  670. foreach (var left in values) {
  671. foreach (var right in values) {
  672. bool evaluatedLeft = false, evaluatedRight = false;
  673. var e = Expression.Lambda<Func<TestShortCircuit?>>(
  674. Expression.OrElse(
  675. Expression.Invoke(
  676. Expression.Constant(
  677. new Func<TestShortCircuit?>(() => { evaluatedLeft = true; return left; })
  678. )
  679. ),
  680. Expression.Invoke(
  681. Expression.Constant(
  682. new Func<TestShortCircuit?>(() => { evaluatedRight = true; return right; })
  683. )
  684. )
  685. )
  686. );
  687. V.Validate(e, f =>
  688. {
  689. var actual = f();
  690. bool expectedEvalRight = false;
  691. var expected = OrElse(
  692. () => { return left; },
  693. () => { expectedEvalRight = true; return right; }
  694. );
  695. EU.Equal(actual, expected);
  696. EU.Equal(evaluatedLeft, true);
  697. EU.Equal(evaluatedRight, expectedEvalRight);
  698. });
  699. }
  700. }
  701. }
  702. private static TestShortCircuit? AndAlso(Func<TestShortCircuit?> getLeft, Func<TestShortCircuit?> getRight) {
  703. TestShortCircuit? left = getLeft();
  704. if (!left.HasValue) {
  705. return null;
  706. }
  707. if (left.GetValueOrDefault().Value == false) {
  708. return left;
  709. }
  710. TestShortCircuit? right = getRight();
  711. if (!right.HasValue) {
  712. return null;
  713. }
  714. return new TestShortCircuit?(left.GetValueOrDefault() & right.GetValueOrDefault());
  715. }
  716. private static TestShortCircuit? OrElse(Func<TestShortCircuit?> getLeft, Func<TestShortCircuit?> getRight) {
  717. TestShortCircuit? left = getLeft();
  718. if (!left.HasValue) {
  719. return null;
  720. }
  721. if (left.GetValueOrDefault().Value == true) {
  722. return left;
  723. }
  724. TestShortCircuit? right = getRight();
  725. if (!right.HasValue) {
  726. return null;
  727. }
  728. return new TestShortCircuit?(left.GetValueOrDefault() | right.GetValueOrDefault());
  729. }
  730. #region Verify that OrElse and AndAlso work when there is a MethodInfo and the 2nd operand has try/catch
  731. public class MyType {
  732. public bool Val { get; set; }
  733. public static bool operator true(MyType x) {
  734. return x.Val;
  735. }
  736. public static bool operator false(MyType x) {
  737. return !(x.Val);
  738. }
  739. public static MyType operator |(MyType x, MyType y) {
  740. return new MyType { Val = x.Val | y.Val };
  741. }
  742. public static MyType operator &(MyType x, MyType y) {
  743. return new MyType { Val = x.Val & y.Val };
  744. }
  745. }
  746. public static void Positive_OrElseWithMethod(EU.IValidator V) {
  747. var x = Expression.Parameter(typeof(MyType), "x");
  748. var y = Expression.Parameter(typeof(MyType), "y");
  749. var le = Expression.Lambda<Func<MyType, MyType, MyType>>(
  750. Expression.Block(
  751. Expression.OrElse(
  752. x,
  753. Expression.Block(
  754. Expression.TryCatch(
  755. Expression.Empty(),
  756. Expression.Catch(
  757. typeof(Exception),
  758. Expression.Empty()
  759. )
  760. ),
  761. y
  762. )
  763. )
  764. ),
  765. x, y
  766. );
  767. V.Validate(le, f =>
  768. {
  769. var @true = new MyType { Val = true };
  770. var @false = new MyType { Val = false };
  771. EU.Equal(f(@true, @true).Val, true);
  772. EU.Equal(f(@true, @false).Val, true);
  773. EU.Equal(f(@false, @true).Val, true);
  774. EU.Equal(f(@false, @false).Val, false);
  775. });
  776. }
  777. public static void Positive_AndAlsoWithMethod(EU.IValidator V) {
  778. var x = Expression.Parameter(typeof(MyType), "x");
  779. var y = Expression.Parameter(typeof(MyType), "y");
  780. var le = Expression.Lambda<Func<MyType, MyType, MyType>>(
  781. Expression.Block(
  782. Expression.AndAlso(
  783. x,
  784. Expression.Block(
  785. Expression.TryCatch(
  786. Expression.Empty(),
  787. Expression.Catch(
  788. typeof(Exception),
  789. Expression.Empty()
  790. )
  791. ),
  792. y
  793. )
  794. )
  795. ),
  796. x, y
  797. );
  798. V.Validate(le, f =>
  799. {
  800. var @true = new MyType { Val = true };
  801. var @false = new MyType { Val = false };
  802. EU.Equal(f(@true, @true).Val, true);
  803. EU.Equal(f(@true, @false).Val, false);
  804. EU.Equal(f(@false, @true).Val, false);
  805. EU.Equal(f(@false, @false).Val, false);
  806. });
  807. }
  808. #endregion
  809. public static void Positive_ConditionalWithType(EU.IValidator V) {
  810. var c = Expression.Parameter(typeof(bool), "c");
  811. string[] one = new[] { "Hi", "Hello" };
  812. ReadOnlyCollection<string> two = new ReadOnlyCollection<string>(one);
  813. // This should fail.
  814. EU.Throws<ArgumentException>(
  815. delegate {
  816. var result = Expression.Condition(
  817. c,
  818. Expression.Constant(one),
  819. Expression.Constant(two)
  820. );
  821. }
  822. );
  823. var le = Expression.Lambda<Func<bool, IList<string>>>(
  824. Expression.Condition(
  825. c,
  826. Expression.Constant(one),
  827. Expression.Constant(two),
  828. typeof(IList<string>)
  829. ),
  830. c
  831. );
  832. V.Validate(le, d =>
  833. {
  834. EU.Equal((object)d(true), (object)one);
  835. EU.Equal((object)d(false), (object)two);
  836. });
  837. }
  838. public static void Positive_UnevenConditionalWithType(EU.IValidator V) {
  839. var c = Expression.Parameter(typeof(bool), "c");
  840. var s = Expression.Parameter(typeof(StrongBox<int>), "s");
  841. var le = Expression.Lambda<Action<bool, StrongBox<int>>>(
  842. Expression.Condition(
  843. c,
  844. Expression.Block(
  845. Expression.Assign(
  846. Expression.Field(s, typeof(StrongBox<int>).GetField("Value")),
  847. Expression.Constant(10)
  848. ),
  849. Expression.Empty()
  850. ),
  851. Expression.Constant("Hello"),
  852. typeof(void)
  853. ),
  854. c, s
  855. );
  856. V.Validate(le, d =>
  857. {
  858. var sb = new StrongBox<int>(0);
  859. // shouldn't change the value of StrongBox
  860. d(false, sb);
  861. EU.Equal(sb.Value, 0);
  862. d(true, sb);
  863. EU.Equal(sb.Value, 10);
  864. });
  865. }
  866. public static void Positive_Bug634624(EU.IValidator V) {
  867. Expression MyAnd = Expression.AndAlso(
  868. Expression.Constant(new AndAlsoTestDerived()),
  869. Expression.Constant(new AndAlsoTest())
  870. );
  871. var l = Expression.Lambda<Func<AndAlsoTest>>(MyAnd);
  872. V.Validate(l, f =>
  873. {
  874. EU.Equal(f(), null);
  875. });
  876. }
  877. public static void Negative_Bug634624(EU.IValidator V) {
  878. EU.Throws<InvalidOperationException>(
  879. () =>
  880. Expression.AndAlso(
  881. Expression.Constant(false),
  882. Expression.Constant(true),
  883. ((Func<bool, bool, bool>)AndAlsoTest.AndAlso).Method
  884. )
  885. );
  886. }
  887. public static void Positive_OptimizedOrElse(EU.IValidator V) {
  888. var e = Expression.Lambda<Func<object>>(
  889. Expression.Call(
  890. typeof(string).GetMethod("Concat", new[] { typeof(object), typeof(object) }),
  891. Expression.Constant("Hello"),
  892. Expression.Condition(
  893. Expression.OrElse(Expression.Constant(true), Expression.Constant(false)),
  894. Expression.Constant(null),
  895. Expression.Constant(null)
  896. )
  897. )
  898. );
  899. var f = e.CompileAndVerify();
  900. Utils.Equals(f(), null);
  901. }
  902. public class AndAlsoTest {
  903. public static bool AndAlso(bool arg1, bool arg2) {
  904. return arg1 && arg2;
  905. }
  906. public static AndAlsoTest operator &(AndAlsoTest arg1, AndAlsoTest arg2) {
  907. return null;
  908. }
  909. public static bool operator true(AndAlsoTest arg1) {
  910. return true;
  911. }
  912. public static bool operator false(AndAlsoTest arg1) {
  913. return false;
  914. }
  915. }
  916. public class AndAlsoTestDerived : AndAlsoTest {
  917. }
  918. }
  919. }
  920. #endif