1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
5 * This source code is subject to terms and conditions of the Microsoft Public License. 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 Microsoft Public License, 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 Microsoft Public License.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
15 using System; using Microsoft;
18 using System.Collections.Generic;
19 using System.Collections.ObjectModel;
20 using System.Diagnostics;
22 using System.Dynamic.Utils;
24 using Microsoft.Scripting.Utils;
28 namespace System.Linq.Expressions {
30 namespace Microsoft.Linq.Expressions {
33 /// Represents a try/catch/finally/fault block.
35 /// The body is protected by the try block.
36 /// The handlers consist of a set of <see cref="CatchBlock"/>s that can either be catch or filters.
37 /// The fault runs if an exception is thrown.
38 /// The finally runs regardless of how control exits the body.
39 /// Only one of fault or finally can be supplied.
40 /// The return type of the try block must match the return type of any associated catch statements.
43 [DebuggerTypeProxy(typeof(Expression.TryExpressionProxy))]
45 public sealed class TryExpression : Expression {
46 private readonly Type _type;
47 private readonly Expression _body;
48 private readonly ReadOnlyCollection<CatchBlock> _handlers;
49 private readonly Expression _finally;
50 private readonly Expression _fault;
52 internal TryExpression(Type type, Expression body, Expression @finally, Expression fault, ReadOnlyCollection<CatchBlock> handlers) {
61 /// Gets the static type of the expression that this <see cref="Expression" /> represents. (Inherited from <see cref="Expression"/>.)
63 /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
64 public sealed override Type Type {
69 /// Returns the node type of this <see cref="Expression" />. (Inherited from <see cref="Expression" />.)
71 /// <returns>The <see cref="ExpressionType"/> that represents this expression.</returns>
72 public sealed override ExpressionType NodeType {
73 get { return ExpressionType.Try; }
77 /// Gets the <see cref="Expression"/> representing the body of the try block.
79 public Expression Body {
84 /// Gets the collection of <see cref="CatchBlock"/>s associated with the try block.
86 public ReadOnlyCollection<CatchBlock> Handlers {
87 get { return _handlers; }
91 /// Gets the <see cref="Expression"/> representing the finally block.
93 public Expression Finally {
94 get { return _finally; }
98 /// Gets the <see cref="Expression"/> representing the fault block.
100 public Expression Fault {
101 get { return _fault; }
104 internal override Expression Accept(ExpressionVisitor visitor) {
105 return visitor.VisitTry(this);
109 public partial class Expression {
112 /// Creates a <see cref="TryExpression"/> representing a try block with a fault block and no catch statements.
114 /// <param name="body">The body of the try block.</param>
115 /// <param name="fault">The body of the fault block.</param>
116 /// <returns>The created <see cref="TryExpression"/>.</returns>
117 public static TryExpression TryFault(Expression body, Expression fault) {
118 return MakeTry(null, body, null, fault, null);
122 /// Creates a <see cref="TryExpression"/> representing a try block with a finally block and no catch statements.
124 /// <param name="body">The body of the try block.</param>
125 /// <param name="finally">The body of the finally block.</param>
126 /// <returns>The created <see cref="TryExpression"/>.</returns>
127 public static TryExpression TryFinally(Expression body, Expression @finally) {
128 return MakeTry(null, body, @finally, null, null);
132 /// Creates a <see cref="TryExpression"/> representing a try block with any number of catch statements and neither a fault nor finally block.
134 /// <param name="body">The body of the try block.</param>
135 /// <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>
136 /// <returns>The created <see cref="TryExpression"/>.</returns>
137 public static TryExpression TryCatch(Expression body, params CatchBlock[] handlers) {
138 return MakeTry(null, body, null, null, handlers);
142 /// Creates a <see cref="TryExpression"/> representing a try block with any number of catch statements and a finally block.
144 /// <param name="body">The body of the try block.</param>
145 /// <param name="finally">The body of the finally block.</param>
146 /// <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>
147 /// <returns>The created <see cref="TryExpression"/>.</returns>
148 public static TryExpression TryCatchFinally(Expression body, Expression @finally, params CatchBlock[] handlers) {
149 return MakeTry(null, body, @finally, null, handlers);
153 /// Creates a <see cref="TryExpression"/> representing a try block with the specified elements.
155 /// <param name="type">The result type of the try expression. If null, bodh and all handlers must have identical type.</param>
156 /// <param name="body">The body of the try block.</param>
157 /// <param name="finally">The body of the finally block. Pass null if the try block has no finally block associated with it.</param>
158 /// <param name="fault">The body of the t block. Pass null if the try block has no fault block associated with it.</param>
159 /// <param name="handlers">A collection of <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 MakeTry(Type type, Expression body, Expression @finally, Expression fault, IEnumerable<CatchBlock> handlers) {
162 RequiresCanRead(body, "body");
164 var @catch = handlers.ToReadOnly();
165 ContractUtils.RequiresNotNullItems(@catch, "handlers");
166 ValidateTryAndCatchHaveSameType(type, body, @catch);
169 if (@finally != null || @catch.Count > 0) {
170 throw Error.FaultCannotHaveCatchOrFinally();
172 RequiresCanRead(fault, "fault");
173 } else if (@finally != null) {
174 RequiresCanRead(@finally, "finally");
175 } else if (@catch.Count == 0) {
176 throw Error.TryMustHaveCatchFinallyOrFault();
179 return new TryExpression(type ?? body.Type, body, @finally, fault, @catch);
182 //Validate that the body of the try expression must have the same type as the body of every try block.
183 private static void ValidateTryAndCatchHaveSameType(Type type, Expression tryBody, ReadOnlyCollection<CatchBlock> handlers) {
184 // Type unification ... all parts must be reference assignable to "type"
186 if (type != typeof(void)) {
187 if (!TypeUtils.AreReferenceAssignable(type, tryBody.Type)) {
188 throw Error.ArgumentTypesMustMatch();
190 foreach (var cb in handlers) {
191 if (!TypeUtils.AreReferenceAssignable(type, cb.Body.Type)) {
192 throw Error.ArgumentTypesMustMatch();
196 } else if (tryBody == null || tryBody.Type == typeof(void)) {
197 //The body of every try block must be null or have void type.
198 foreach (CatchBlock cb in handlers) {
199 if (cb.Body != null && cb.Body.Type != typeof(void)) {
200 throw Error.BodyOfCatchMustHaveSameTypeAsBodyOfTry();
204 //Body of every catch must have the same type of body of try.
206 foreach (CatchBlock cb in handlers) {
207 if (cb.Body == null || !TypeUtils.AreEquivalent(cb.Body.Type, type)) {
208 throw Error.BodyOfCatchMustHaveSameTypeAsBodyOfTry();