1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
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.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
17 using System.Collections.Generic;
18 using System.Collections.ObjectModel;
19 using System.Diagnostics;
20 using System.Dynamic.Utils;
23 namespace Microsoft.Scripting.Ast {
25 namespace System.Linq.Expressions {
28 /// Represents a try/catch/finally/fault block.
30 /// The body is protected by the try block.
31 /// The handlers consist of a set of <see cref="CatchBlock"/>s that can either be catch or filters.
32 /// The fault runs if an exception is thrown.
33 /// The finally runs regardless of how control exits the body.
34 /// Only one of fault or finally can be supplied.
35 /// The return type of the try block must match the return type of any associated catch statements.
37 [DebuggerTypeProxy(typeof(Expression.TryExpressionProxy))]
38 public sealed class TryExpression : Expression {
39 private readonly Type _type;
40 private readonly Expression _body;
41 private readonly ReadOnlyCollection<CatchBlock> _handlers;
42 private readonly Expression _finally;
43 private readonly Expression _fault;
45 internal TryExpression(Type type, Expression body, Expression @finally, Expression fault, ReadOnlyCollection<CatchBlock> handlers) {
54 /// Gets the static type of the expression that this <see cref="Expression" /> represents. (Inherited from <see cref="Expression"/>.)
56 /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
57 public sealed override Type Type {
62 /// Returns the node type of this <see cref="Expression" />. (Inherited from <see cref="Expression" />.)
64 /// <returns>The <see cref="ExpressionType"/> that represents this expression.</returns>
65 public sealed override ExpressionType NodeType {
66 get { return ExpressionType.Try; }
70 /// Gets the <see cref="Expression"/> representing the body of the try block.
72 public Expression Body {
77 /// Gets the collection of <see cref="CatchBlock"/>s associated with the try block.
79 public ReadOnlyCollection<CatchBlock> Handlers {
80 get { return _handlers; }
84 /// Gets the <see cref="Expression"/> representing the finally block.
86 public Expression Finally {
87 get { return _finally; }
91 /// Gets the <see cref="Expression"/> representing the fault block.
93 public Expression Fault {
94 get { return _fault; }
98 /// Dispatches to the specific visit method for this node type.
100 protected internal override Expression Accept(ExpressionVisitor visitor) {
101 return visitor.VisitTry(this);
105 /// Creates a new expression that is like this one, but using the
106 /// supplied children. If all of the children are the same, it will
107 /// return this expression.
109 /// <param name="body">The <see cref="Body" /> property of the result.</param>
110 /// <param name="handlers">The <see cref="Handlers" /> property of the result.</param>
111 /// <param name="finally">The <see cref="Finally" /> property of the result.</param>
112 /// <param name="fault">The <see cref="Fault" /> property of the result.</param>
113 /// <returns>This expression if no children changed, or an expression with the updated children.</returns>
114 public TryExpression Update(Expression body, IEnumerable<CatchBlock> handlers, Expression @finally, Expression fault) {
115 if (body == Body && handlers == Handlers && @finally == Finally && fault == Fault) {
118 return Expression.MakeTry(Type, body, @finally, fault, handlers);
122 public partial class Expression {
125 /// Creates a <see cref="TryExpression"/> representing a try block with a fault block and no catch statements.
127 /// <param name="body">The body of the try block.</param>
128 /// <param name="fault">The body of the fault block.</param>
129 /// <returns>The created <see cref="TryExpression"/>.</returns>
130 public static TryExpression TryFault(Expression body, Expression fault) {
131 return MakeTry(null, body, null, fault, null);
135 /// Creates a <see cref="TryExpression"/> representing a try block with a finally block and no catch statements.
137 /// <param name="body">The body of the try block.</param>
138 /// <param name="finally">The body of the finally block.</param>
139 /// <returns>The created <see cref="TryExpression"/>.</returns>
140 public static TryExpression TryFinally(Expression body, Expression @finally) {
141 return MakeTry(null, body, @finally, null, null);
145 /// Creates a <see cref="TryExpression"/> representing a try block with any number of catch statements and neither a fault nor finally block.
147 /// <param name="body">The body of the try block.</param>
148 /// <param name="handlers">The array of zero or more <see cref="CatchBlock"/>s representing the catch statements to be associated with the try block.</param>
149 /// <returns>The created <see cref="TryExpression"/>.</returns>
150 public static TryExpression TryCatch(Expression body, params CatchBlock[] handlers) {
151 return MakeTry(null, body, null, null, handlers);
155 /// Creates a <see cref="TryExpression"/> representing a try block with any number of catch statements and a finally block.
157 /// <param name="body">The body of the try block.</param>
158 /// <param name="finally">The body of the finally block.</param>
159 /// <param name="handlers">The array of zero or more <see cref="CatchBlock"/>s representing the catch statements to be associated with the try block.</param>
160 /// <returns>The created <see cref="TryExpression"/>.</returns>
161 public static TryExpression TryCatchFinally(Expression body, Expression @finally, params CatchBlock[] handlers) {
162 return MakeTry(null, body, @finally, null, handlers);
166 /// Creates a <see cref="TryExpression"/> representing a try block with the specified elements.
168 /// <param name="type">The result type of the try expression. If null, bodh and all handlers must have identical type.</param>
169 /// <param name="body">The body of the try block.</param>
170 /// <param name="finally">The body of the finally block. Pass null if the try block has no finally block associated with it.</param>
171 /// <param name="fault">The body of the t block. Pass null if the try block has no fault block associated with it.</param>
172 /// <param name="handlers">A collection of <see cref="CatchBlock"/>s representing the catch statements to be associated with the try block.</param>
173 /// <returns>The created <see cref="TryExpression"/>.</returns>
174 public static TryExpression MakeTry(Type type, Expression body, Expression @finally, Expression fault, IEnumerable<CatchBlock> handlers) {
175 RequiresCanRead(body, "body");
177 var @catch = handlers.ToReadOnly();
178 ContractUtils.RequiresNotNullItems(@catch, "handlers");
179 ValidateTryAndCatchHaveSameType(type, body, @catch);
182 if (@finally != null || @catch.Count > 0) {
183 throw Error.FaultCannotHaveCatchOrFinally();
185 RequiresCanRead(fault, "fault");
186 } else if (@finally != null) {
187 RequiresCanRead(@finally, "finally");
188 } else if (@catch.Count == 0) {
189 throw Error.TryMustHaveCatchFinallyOrFault();
192 return new TryExpression(type ?? body.Type, body, @finally, fault, @catch);
195 //Validate that the body of the try expression must have the same type as the body of every try block.
196 private static void ValidateTryAndCatchHaveSameType(Type type, Expression tryBody, ReadOnlyCollection<CatchBlock> handlers) {
197 // Type unification ... all parts must be reference assignable to "type"
199 if (type != typeof(void)) {
200 if (!TypeUtils.AreReferenceAssignable(type, tryBody.Type)) {
201 throw Error.ArgumentTypesMustMatch();
203 foreach (var cb in handlers) {
204 if (!TypeUtils.AreReferenceAssignable(type, cb.Body.Type)) {
205 throw Error.ArgumentTypesMustMatch();
209 } else if (tryBody == null || tryBody.Type == typeof(void)) {
210 //The body of every try block must be null or have void type.
211 foreach (CatchBlock cb in handlers) {
212 if (cb.Body != null && cb.Body.Type != typeof(void)) {
213 throw Error.BodyOfCatchMustHaveSameTypeAsBodyOfTry();
217 //Body of every catch must have the same type of body of try.
219 foreach (CatchBlock cb in handlers) {
220 if (cb.Body == null || !TypeUtils.AreEquivalent(cb.Body.Type, type)) {
221 throw Error.BodyOfCatchMustHaveSameTypeAsBodyOfTry();