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

/IronPython_Main/Runtime/Microsoft.Scripting.Core/Ast/SwitchExpression.cs

#
C# | 304 lines | 160 code | 30 blank | 114 comment | 39 complexity | 87db5fd622663a5fda8739e167feb3fe 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. using System;
  16. using System.Collections.Generic;
  17. using System.Collections.ObjectModel;
  18. using System.Diagnostics;
  19. using System.Dynamic.Utils;
  20. using System.Reflection;
  21. #if SILVERLIGHT
  22. using System.Core;
  23. #endif
  24. #if CLR2
  25. namespace Microsoft.Scripting.Ast {
  26. #else
  27. namespace System.Linq.Expressions {
  28. #endif
  29. /// <summary>
  30. /// Represents a control expression that handles multiple selections by passing control to a <see cref="SwitchCase"/>.
  31. /// </summary>
  32. #if !SILVERLIGHT
  33. [DebuggerTypeProxy(typeof(Expression.SwitchExpressionProxy))]
  34. #endif
  35. public sealed class SwitchExpression : Expression {
  36. private readonly Type _type;
  37. private readonly Expression _switchValue;
  38. private readonly ReadOnlyCollection<SwitchCase> _cases;
  39. private readonly Expression _defaultBody;
  40. private readonly MethodInfo _comparison;
  41. internal SwitchExpression(Type type, Expression switchValue, Expression defaultBody, MethodInfo comparison, ReadOnlyCollection<SwitchCase> cases) {
  42. _type = type;
  43. _switchValue = switchValue;
  44. _defaultBody = defaultBody;
  45. _comparison = comparison;
  46. _cases = cases;
  47. }
  48. /// <summary>
  49. /// Gets the static type of the expression that this <see cref="Expression" /> represents.
  50. /// </summary>
  51. /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
  52. public sealed override Type Type {
  53. get { return _type; }
  54. }
  55. /// <summary>
  56. /// Returns the node type of this Expression. Extension nodes should return
  57. /// ExpressionType.Extension when overriding this method.
  58. /// </summary>
  59. /// <returns>The <see cref="ExpressionType"/> of the expression.</returns>
  60. public sealed override ExpressionType NodeType {
  61. get { return ExpressionType.Switch; }
  62. }
  63. /// <summary>
  64. /// Gets the test for the switch.
  65. /// </summary>
  66. public Expression SwitchValue {
  67. get { return _switchValue; }
  68. }
  69. /// <summary>
  70. /// Gets the collection of <see cref="SwitchCase"/> objects for the switch.
  71. /// </summary>
  72. public ReadOnlyCollection<SwitchCase> Cases {
  73. get { return _cases; }
  74. }
  75. /// <summary>
  76. /// Gets the test for the switch.
  77. /// </summary>
  78. public Expression DefaultBody {
  79. get { return _defaultBody; }
  80. }
  81. /// <summary>
  82. /// Gets the equality comparison method, if any.
  83. /// </summary>
  84. public MethodInfo Comparison {
  85. get { return _comparison; }
  86. }
  87. /// <summary>
  88. /// Dispatches to the specific visit method for this node type.
  89. /// </summary>
  90. protected internal override Expression Accept(ExpressionVisitor visitor) {
  91. return visitor.VisitSwitch(this);
  92. }
  93. internal bool IsLifted {
  94. get {
  95. if (_switchValue.Type.IsNullableType()) {
  96. return (_comparison == null) ||
  97. !TypeUtils.AreEquivalent(_switchValue.Type, _comparison.GetParametersCached()[0].ParameterType.GetNonRefType());
  98. }
  99. return false;
  100. }
  101. }
  102. /// <summary>
  103. /// Creates a new expression that is like this one, but using the
  104. /// supplied children. If all of the children are the same, it will
  105. /// return this expression.
  106. /// </summary>
  107. /// <param name="switchValue">The <see cref="SwitchValue" /> property of the result.</param>
  108. /// <param name="cases">The <see cref="Cases" /> property of the result.</param>
  109. /// <param name="defaultBody">The <see cref="DefaultBody" /> property of the result.</param>
  110. /// <returns>This expression if no children changed, or an expression with the updated children.</returns>
  111. public SwitchExpression Update(Expression switchValue, IEnumerable<SwitchCase> cases, Expression defaultBody) {
  112. if (switchValue == SwitchValue && cases == Cases && defaultBody == DefaultBody) {
  113. return this;
  114. }
  115. return Expression.Switch(Type, switchValue, defaultBody, Comparison, cases);
  116. }
  117. }
  118. public partial class Expression {
  119. /// <summary>
  120. /// Creates a <see cref="SwitchExpression"/>.
  121. /// </summary>
  122. /// <param name="switchValue">The value to be tested against each case.</param>
  123. /// <param name="cases">The valid cases for this switch.</param>
  124. /// <returns>The created <see cref="SwitchExpression"/>.</returns>
  125. public static SwitchExpression Switch(Expression switchValue, params SwitchCase[] cases) {
  126. return Switch(switchValue, null, null, (IEnumerable<SwitchCase>)cases);
  127. }
  128. /// <summary>
  129. /// Creates a <see cref="SwitchExpression"/>.
  130. /// </summary>
  131. /// <param name="switchValue">The value to be tested against each case.</param>
  132. /// <param name="defaultBody">The result of the switch if no cases are matched.</param>
  133. /// <param name="cases">The valid cases for this switch.</param>
  134. /// <returns>The created <see cref="SwitchExpression"/>.</returns>
  135. public static SwitchExpression Switch(Expression switchValue, Expression defaultBody, params SwitchCase[] cases) {
  136. return Switch(switchValue, defaultBody, null, (IEnumerable<SwitchCase>)cases);
  137. }
  138. /// <summary>
  139. /// Creates a <see cref="SwitchExpression"/>.
  140. /// </summary>
  141. /// <param name="switchValue">The value to be tested against each case.</param>
  142. /// <param name="defaultBody">The result of the switch if no cases are matched.</param>
  143. /// <param name="comparison">The equality comparison method to use.</param>
  144. /// <param name="cases">The valid cases for this switch.</param>
  145. /// <returns>The created <see cref="SwitchExpression"/>.</returns>
  146. public static SwitchExpression Switch(Expression switchValue, Expression defaultBody, MethodInfo comparison, params SwitchCase[] cases) {
  147. return Switch(switchValue, defaultBody, comparison, (IEnumerable<SwitchCase>)cases);
  148. }
  149. /// <summary>
  150. /// Creates a <see cref="SwitchExpression"/>.
  151. /// </summary>
  152. /// <param name="type">The result type of the switch.</param>
  153. /// <param name="switchValue">The value to be tested against each case.</param>
  154. /// <param name="defaultBody">The result of the switch if no cases are matched.</param>
  155. /// <param name="comparison">The equality comparison method to use.</param>
  156. /// <param name="cases">The valid cases for this switch.</param>
  157. /// <returns>The created <see cref="SwitchExpression"/>.</returns>
  158. public static SwitchExpression Switch(Type type, Expression switchValue, Expression defaultBody, MethodInfo comparison, params SwitchCase[] cases) {
  159. return Switch(type, switchValue, defaultBody, comparison, (IEnumerable<SwitchCase>)cases);
  160. }
  161. /// <summary>
  162. /// Creates a <see cref="SwitchExpression"/>.
  163. /// </summary>
  164. /// <param name="switchValue">The value to be tested against each case.</param>
  165. /// <param name="defaultBody">The result of the switch if no cases are matched.</param>
  166. /// <param name="comparison">The equality comparison method to use.</param>
  167. /// <param name="cases">The valid cases for this switch.</param>
  168. /// <returns>The created <see cref="SwitchExpression"/>.</returns>
  169. public static SwitchExpression Switch(Expression switchValue, Expression defaultBody, MethodInfo comparison, IEnumerable<SwitchCase> cases) {
  170. return Switch(null, switchValue, defaultBody, comparison, cases);
  171. }
  172. /// <summary>
  173. /// Creates a <see cref="SwitchExpression"/>.
  174. /// </summary>
  175. /// <param name="type">The result type of the switch.</param>
  176. /// <param name="switchValue">The value to be tested against each case.</param>
  177. /// <param name="defaultBody">The result of the switch if no cases are matched.</param>
  178. /// <param name="comparison">The equality comparison method to use.</param>
  179. /// <param name="cases">The valid cases for this switch.</param>
  180. /// <returns>The created <see cref="SwitchExpression"/>.</returns>
  181. public static SwitchExpression Switch(Type type, Expression switchValue, Expression defaultBody, MethodInfo comparison, IEnumerable<SwitchCase> cases) {
  182. RequiresCanRead(switchValue, "switchValue");
  183. if (switchValue.Type == typeof(void)) throw Error.ArgumentCannotBeOfTypeVoid();
  184. var caseList = cases.ToReadOnly();
  185. ContractUtils.RequiresNotEmpty(caseList, "cases");
  186. ContractUtils.RequiresNotNullItems(caseList, "cases");
  187. // Type of the result. Either provided, or it is type of the branches.
  188. Type resultType = type ?? caseList[0].Body.Type;
  189. bool customType = type != null;
  190. if (comparison != null) {
  191. var pms = comparison.GetParametersCached();
  192. if (pms.Length != 2) {
  193. throw Error.IncorrectNumberOfMethodCallArguments(comparison);
  194. }
  195. // Validate that the switch value's type matches the comparison method's
  196. // left hand side parameter type.
  197. var leftParam = pms[0];
  198. bool liftedCall = false;
  199. if (!ParameterIsAssignable(leftParam, switchValue.Type)) {
  200. liftedCall = ParameterIsAssignable(leftParam, switchValue.Type.GetNonNullableType());
  201. if (!liftedCall) {
  202. throw Error.SwitchValueTypeDoesNotMatchComparisonMethodParameter(switchValue.Type, leftParam.ParameterType);
  203. }
  204. }
  205. var rightParam = pms[1];
  206. foreach (var c in caseList) {
  207. ContractUtils.RequiresNotNull(c, "cases");
  208. ValidateSwitchCaseType(c.Body, customType, resultType, "cases");
  209. for (int i = 0; i < c.TestValues.Count; i++) {
  210. // When a comparison method is provided, test values can have different type but have to
  211. // be reference assignable to the right hand side parameter of the method.
  212. Type rightOperandType = c.TestValues[i].Type;
  213. if (liftedCall) {
  214. if (!rightOperandType.IsNullableType()) {
  215. throw Error.TestValueTypeDoesNotMatchComparisonMethodParameter(rightOperandType, rightParam.ParameterType);
  216. }
  217. rightOperandType = rightOperandType.GetNonNullableType();
  218. }
  219. if (!ParameterIsAssignable(rightParam, rightOperandType)) {
  220. throw Error.TestValueTypeDoesNotMatchComparisonMethodParameter(rightOperandType, rightParam.ParameterType);
  221. }
  222. }
  223. }
  224. } else {
  225. // When comparison method is not present, all the test values must have
  226. // the same type. Use the first test value's type as the baseline.
  227. var firstTestValue = caseList[0].TestValues[0];
  228. foreach (var c in caseList) {
  229. ContractUtils.RequiresNotNull(c, "cases");
  230. ValidateSwitchCaseType(c.Body, customType, resultType, "cases");
  231. // When no comparison method is provided, require all test values to have the same type.
  232. for (int i = 0; i < c.TestValues.Count; i++) {
  233. if (!TypeUtils.AreEquivalent(firstTestValue.Type, c.TestValues[i].Type)) {
  234. throw new ArgumentException(Strings.AllTestValuesMustHaveSameType, "cases");
  235. }
  236. }
  237. }
  238. // Now we need to validate that switchValue.Type and testValueType
  239. // make sense in an Equal node. Fortunately, Equal throws a
  240. // reasonable error, so just call it.
  241. var equal = Equal(switchValue, firstTestValue, false, comparison);
  242. // Get the comparison function from equals node.
  243. comparison = equal.Method;
  244. }
  245. if (defaultBody == null) {
  246. if (resultType != typeof(void)) throw Error.DefaultBodyMustBeSupplied();
  247. } else {
  248. ValidateSwitchCaseType(defaultBody, customType, resultType, "defaultBody");
  249. }
  250. // if we have a non-boolean userdefined equals, we don't want it.
  251. if (comparison != null && comparison.ReturnType != typeof(bool)) {
  252. throw Error.EqualityMustReturnBoolean(comparison);
  253. }
  254. return new SwitchExpression(resultType, switchValue, defaultBody, comparison, caseList);
  255. }
  256. /// <summary>
  257. /// If custom type is provided, all branches must be reference assignable to the result type.
  258. /// If no custom type is provided, all branches must have the same type - resultType.
  259. /// </summary>
  260. private static void ValidateSwitchCaseType(Expression @case, bool customType, Type resultType, string parameterName) {
  261. if (customType) {
  262. if (resultType != typeof(void)) {
  263. if (!TypeUtils.AreReferenceAssignable(resultType, @case.Type)) {
  264. throw new ArgumentException(Strings.ArgumentTypesMustMatch, parameterName);
  265. }
  266. }
  267. } else {
  268. if (!TypeUtils.AreEquivalent(resultType, @case.Type)) {
  269. throw new ArgumentException(Strings.AllCaseBodiesMustHaveSameType, parameterName);
  270. }
  271. }
  272. }
  273. }
  274. }