PageRenderTime 45ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/DLR/Microsoft.Dynamic/Ast/TryStatementBuilder.cs

#
C# | 230 lines | 162 code | 43 blank | 25 comment | 24 complexity | 57d1dfe94cc25093b64160f618125a2a MD5 | raw file
Possible License(s): BSD-3-Clause
  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.Linq.Expressions;
  16. using System;
  17. using System.Collections.Generic;
  18. using System.Dynamic;
  19. using Microsoft.Scripting.Utils;
  20. namespace Microsoft.Scripting.Ast {
  21. public sealed class TryStatementBuilder {
  22. private readonly List<CatchBlock> _catchBlocks = new List<CatchBlock>();
  23. private Expression _try;
  24. private Expression _finally, _fault;
  25. private bool _enableJumpsFromFinally;
  26. internal TryStatementBuilder(Expression body) {
  27. _try = body;
  28. }
  29. public TryStatementBuilder Catch(Type type, Expression body) {
  30. ContractUtils.RequiresNotNull(type, "type");
  31. ContractUtils.RequiresNotNull(body, "body");
  32. if (_finally != null) {
  33. throw Error.FinallyAlreadyDefined();
  34. }
  35. _catchBlocks.Add(Expression.Catch(type, body));
  36. return this;
  37. }
  38. public TryStatementBuilder Catch(Type type, Expression expr0, Expression expr1) {
  39. return Catch(type, Expression.Block(expr0, expr1));
  40. }
  41. public TryStatementBuilder Catch(Type type, Expression expr0, Expression expr1, Expression expr2) {
  42. return Catch(type, Expression.Block(expr0, expr1, expr2));
  43. }
  44. public TryStatementBuilder Catch(Type type, Expression expr0, Expression expr1, Expression expr2, Expression expr3) {
  45. return Catch(type, Expression.Block(expr0, expr1, expr2, expr3));
  46. }
  47. public TryStatementBuilder Catch(Type type, params Expression[] body) {
  48. return Catch(type, Expression.Block(body));
  49. }
  50. public TryStatementBuilder Catch(ParameterExpression holder, Expression expr0, Expression expr1) {
  51. return Catch(holder, Expression.Block(expr0, expr1));
  52. }
  53. public TryStatementBuilder Catch(ParameterExpression holder, Expression expr0, Expression expr1, Expression expr2) {
  54. return Catch(holder, Expression.Block(expr0, expr1, expr2));
  55. }
  56. public TryStatementBuilder Catch(ParameterExpression holder, Expression expr0, Expression expr1, Expression expr2, Expression expr3) {
  57. return Catch(holder, Expression.Block(expr0, expr1, expr2, expr3));
  58. }
  59. public TryStatementBuilder Catch(ParameterExpression holder, params Expression[] body) {
  60. return Catch(holder, Utils.Block(body));
  61. }
  62. public TryStatementBuilder Catch(ParameterExpression holder, Expression body) {
  63. ContractUtils.RequiresNotNull(holder, "holder");
  64. ContractUtils.RequiresNotNull(body, "body");
  65. if (_finally != null) {
  66. throw Error.FinallyAlreadyDefined();
  67. }
  68. _catchBlocks.Add(Expression.Catch(holder, body));
  69. return this;
  70. }
  71. public TryStatementBuilder Filter(Type type, Expression condition, params Expression[] body) {
  72. return Filter(type, condition, Utils.Block(body));
  73. }
  74. public TryStatementBuilder Filter(Type type, Expression condition, Expression body) {
  75. ContractUtils.RequiresNotNull(type, "type");
  76. ContractUtils.RequiresNotNull(condition, "condition");
  77. ContractUtils.RequiresNotNull(body, "body");
  78. _catchBlocks.Add(Expression.Catch(type, body, condition));
  79. return this;
  80. }
  81. public TryStatementBuilder Filter(ParameterExpression holder, Expression condition, params Expression[] body) {
  82. return Filter(holder, condition, Utils.Block(body));
  83. }
  84. public TryStatementBuilder Filter(ParameterExpression holder, Expression condition, Expression body) {
  85. ContractUtils.RequiresNotNull(holder, "holder");
  86. ContractUtils.RequiresNotNull(condition, "condition");
  87. ContractUtils.RequiresNotNull(body, "body");
  88. _catchBlocks.Add(Expression.Catch(holder, body, condition));
  89. return this;
  90. }
  91. public TryStatementBuilder Finally(params Expression[] body) {
  92. return Finally(Utils.BlockVoid(body));
  93. }
  94. public TryStatementBuilder Finally(Expression body) {
  95. ContractUtils.RequiresNotNull(body, "body");
  96. if (_finally != null) {
  97. throw Error.FinallyAlreadyDefined();
  98. } else if (_fault != null) {
  99. throw Error.CannotHaveFaultAndFinally();
  100. }
  101. _finally = body;
  102. return this;
  103. }
  104. public TryStatementBuilder FinallyWithJumps(params Expression[] body) {
  105. _enableJumpsFromFinally = true;
  106. return Finally(body);
  107. }
  108. public TryStatementBuilder FinallyWithJumps(Expression body) {
  109. _enableJumpsFromFinally = true;
  110. return Finally(body);
  111. }
  112. public TryStatementBuilder Fault(params Expression[] body) {
  113. ContractUtils.RequiresNotNullItems(body, "body");
  114. if (_finally != null) {
  115. throw Error.CannotHaveFaultAndFinally();
  116. } else if (_fault != null) {
  117. throw Error.FaultAlreadyDefined();
  118. }
  119. if (body.Length == 1) {
  120. _fault = body[0];
  121. } else {
  122. _fault = Utils.BlockVoid(body);
  123. }
  124. return this;
  125. }
  126. public static implicit operator Expression(TryStatementBuilder builder) {
  127. ContractUtils.RequiresNotNull(builder, "builder");
  128. return builder.ToExpression();
  129. }
  130. public Expression ToExpression() {
  131. //
  132. // We can't emit a real filter or fault because they don't
  133. // work in DynamicMethods. Instead we do a simple transformation:
  134. // fault -> catch (Exception) { ...; rethrow }
  135. // filter -> catch (ExceptionType) { if (!filter) rethrow; ... }
  136. //
  137. // Note that the filter transformation is not quite equivalent to
  138. // real CLR semantics, but it's what IronPython and IronRuby
  139. // expect. If we get CLR support we'll switch over to real filter
  140. // and fault blocks.
  141. //
  142. var handlers = new List<CatchBlock>(_catchBlocks);
  143. for (int i = 0, n = handlers.Count; i < n ; i++) {
  144. CatchBlock handler = handlers[i];
  145. if (handler.Filter != null) {
  146. handlers[i] = Expression.MakeCatchBlock(
  147. handler.Test,
  148. handler.Variable,
  149. Expression.Condition(
  150. handler.Filter,
  151. handler.Body,
  152. Expression.Rethrow(handler.Body.Type)
  153. ),
  154. null
  155. );
  156. }
  157. }
  158. if (_fault != null) {
  159. ContractUtils.Requires(handlers.Count == 0, "fault cannot be used with catch or finally clauses");
  160. handlers.Add(Expression.Catch(typeof(Exception), Expression.Block(_fault, Expression.Rethrow(_try.Type))));
  161. }
  162. var result = Expression.MakeTry(null, _try, _finally, null, handlers);
  163. if (_enableJumpsFromFinally) {
  164. return Utils.FinallyFlowControl(result);
  165. }
  166. return result;
  167. }
  168. }
  169. public partial class Utils {
  170. public static TryStatementBuilder Try(Expression body) {
  171. ContractUtils.RequiresNotNull(body, "body");
  172. return new TryStatementBuilder(body);
  173. }
  174. public static TryStatementBuilder Try(Expression expr0, Expression expr1) {
  175. return new TryStatementBuilder(Expression.Block(expr0, expr1));
  176. }
  177. public static TryStatementBuilder Try(Expression expr0, Expression expr1, Expression expr2) {
  178. return new TryStatementBuilder(Expression.Block(expr0, expr1, expr2));
  179. }
  180. public static TryStatementBuilder Try(Expression expr0, Expression expr1, Expression expr2, Expression expr3) {
  181. return new TryStatementBuilder(Expression.Block(expr0, expr1, expr2, expr3));
  182. }
  183. public static TryStatementBuilder Try(params Expression[] body) {
  184. return new TryStatementBuilder(Expression.Block(body));
  185. }
  186. }
  187. }