2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Ast / TryExpression.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
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.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15 using System; using Microsoft;
16
17
18 using System.Collections.Generic;
19 using System.Collections.ObjectModel;
20 using System.Diagnostics;
21 #if CODEPLEX_40
22 using System.Dynamic.Utils;
23 #else
24 using Microsoft.Scripting.Utils;
25 #endif
26
27 #if CODEPLEX_40
28 namespace System.Linq.Expressions {
29 #else
30 namespace Microsoft.Linq.Expressions {
31 #endif
32     /// <summary>
33     /// Represents a try/catch/finally/fault block.
34     /// 
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.
41     /// </summary>
42 #if !SILVERLIGHT
43     [DebuggerTypeProxy(typeof(Expression.TryExpressionProxy))]
44 #endif
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;
51
52         internal TryExpression(Type type, Expression body, Expression @finally, Expression fault, ReadOnlyCollection<CatchBlock> handlers) {
53             _type = type;
54             _body = body;
55             _handlers = handlers;
56             _finally = @finally;
57             _fault = fault;
58         }
59
60         /// <summary>
61         /// Gets the static type of the expression that this <see cref="Expression" /> represents. (Inherited from <see cref="Expression"/>.)
62         /// </summary>
63         /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
64         public sealed override Type Type {
65             get { return _type; }
66         }
67
68         /// <summary>
69         /// Returns the node type of this <see cref="Expression" />. (Inherited from <see cref="Expression" />.)
70         /// </summary>
71         /// <returns>The <see cref="ExpressionType"/> that represents this expression.</returns>
72         public sealed override ExpressionType NodeType {
73             get { return ExpressionType.Try; }
74         }
75
76         /// <summary>
77         /// Gets the <see cref="Expression"/> representing the body of the try block.
78         /// </summary>
79         public Expression Body {
80             get { return _body; }
81         }
82
83         /// <summary>
84         /// Gets the collection of <see cref="CatchBlock"/>s associated with the try block.
85         /// </summary>
86         public ReadOnlyCollection<CatchBlock> Handlers {
87             get { return _handlers; }
88         }
89
90         /// <summary>
91         /// Gets the <see cref="Expression"/> representing the finally block.
92         /// </summary>
93         public Expression Finally {
94             get { return _finally; }
95         }
96
97         /// <summary>
98         /// Gets the <see cref="Expression"/> representing the fault block.
99         /// </summary>
100         public Expression Fault {
101             get { return _fault; }
102         }
103
104         internal override Expression Accept(ExpressionVisitor visitor) {
105             return visitor.VisitTry(this);
106         }
107     }
108
109     public partial class Expression {
110
111         /// <summary>
112         /// Creates a <see cref="TryExpression"/> representing a try block with a fault block and no catch statements.
113         /// </summary>
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);
119         }
120
121         /// <summary>
122         /// Creates a <see cref="TryExpression"/> representing a try block with a finally block and no catch statements.
123         /// </summary>
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);
129         }
130
131         /// <summary>
132         /// Creates a <see cref="TryExpression"/> representing a try block with any number of catch statements and neither a fault nor finally block.
133         /// </summary>
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);
139         }
140
141         /// <summary>
142         /// Creates a <see cref="TryExpression"/> representing a try block with any number of catch statements and a finally block.
143         /// </summary>
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);
150         }
151
152         /// <summary>
153         /// Creates a <see cref="TryExpression"/> representing a try block with the specified elements.
154         /// </summary>
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");
163
164             var @catch = handlers.ToReadOnly();
165             ContractUtils.RequiresNotNullItems(@catch, "handlers");
166             ValidateTryAndCatchHaveSameType(type, body, @catch);
167
168             if (fault != null) {
169                 if (@finally != null || @catch.Count > 0) {
170                     throw Error.FaultCannotHaveCatchOrFinally();
171                 }
172                 RequiresCanRead(fault, "fault");
173             } else if (@finally != null) {
174                 RequiresCanRead(@finally, "finally");
175             } else if (@catch.Count == 0) {
176                 throw Error.TryMustHaveCatchFinallyOrFault();
177             }
178
179             return new TryExpression(type ?? body.Type, body, @finally, fault, @catch);
180         }
181
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"
185             if (type != null) {
186                 if (type != typeof(void)) {
187                     if (!TypeUtils.AreReferenceAssignable(type, tryBody.Type)) {
188                         throw Error.ArgumentTypesMustMatch();
189                     }
190                     foreach (var cb in handlers) {
191                         if (!TypeUtils.AreReferenceAssignable(type, cb.Body.Type)) {
192                             throw Error.ArgumentTypesMustMatch();
193                         }
194                     }
195                 }
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();
201                     }
202                 }
203             } else {
204                 //Body of every catch must have the same type of body of try.
205                 type = tryBody.Type;
206                 foreach (CatchBlock cb in handlers) {
207                     if (cb.Body == null || !TypeUtils.AreEquivalent(cb.Body.Type, type)) {
208                         throw Error.BodyOfCatchMustHaveSameTypeAsBodyOfTry();
209                     }
210                 }
211             }
212         }
213     }
214
215 }