Merge pull request #439 from mono-soc-2012/garyb/iconfix
[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 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
16 using System;
17 using System.Collections.Generic;
18 using System.Collections.ObjectModel;
19 using System.Diagnostics;
20 using System.Dynamic.Utils;
21
22 #if !FEATURE_CORE_DLR
23 namespace Microsoft.Scripting.Ast {
24 #else
25 namespace System.Linq.Expressions {
26 #endif
27     /// <summary>
28     /// Represents a try/catch/finally/fault block.
29     /// 
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.
36     /// </summary>
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;
44
45         internal TryExpression(Type type, Expression body, Expression @finally, Expression fault, ReadOnlyCollection<CatchBlock> handlers) {
46             _type = type;
47             _body = body;
48             _handlers = handlers;
49             _finally = @finally;
50             _fault = fault;
51         }
52
53         /// <summary>
54         /// Gets the static type of the expression that this <see cref="Expression" /> represents. (Inherited from <see cref="Expression"/>.)
55         /// </summary>
56         /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
57         public sealed override Type Type {
58             get { return _type; }
59         }
60
61         /// <summary>
62         /// Returns the node type of this <see cref="Expression" />. (Inherited from <see cref="Expression" />.)
63         /// </summary>
64         /// <returns>The <see cref="ExpressionType"/> that represents this expression.</returns>
65         public sealed override ExpressionType NodeType {
66             get { return ExpressionType.Try; }
67         }
68
69         /// <summary>
70         /// Gets the <see cref="Expression"/> representing the body of the try block.
71         /// </summary>
72         public Expression Body {
73             get { return _body; }
74         }
75
76         /// <summary>
77         /// Gets the collection of <see cref="CatchBlock"/>s associated with the try block.
78         /// </summary>
79         public ReadOnlyCollection<CatchBlock> Handlers {
80             get { return _handlers; }
81         }
82
83         /// <summary>
84         /// Gets the <see cref="Expression"/> representing the finally block.
85         /// </summary>
86         public Expression Finally {
87             get { return _finally; }
88         }
89
90         /// <summary>
91         /// Gets the <see cref="Expression"/> representing the fault block.
92         /// </summary>
93         public Expression Fault {
94             get { return _fault; }
95         }
96
97         /// <summary>
98         /// Dispatches to the specific visit method for this node type.
99         /// </summary>
100         protected internal override Expression Accept(ExpressionVisitor visitor) {
101             return visitor.VisitTry(this);
102         }
103
104         /// <summary>
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.
108         /// </summary>
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) {
116                 return this;
117             }
118             return Expression.MakeTry(Type, body, @finally, fault, handlers);
119         }
120     }
121
122     public partial class Expression {
123
124         /// <summary>
125         /// Creates a <see cref="TryExpression"/> representing a try block with a fault block and no catch statements.
126         /// </summary>
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);
132         }
133
134         /// <summary>
135         /// Creates a <see cref="TryExpression"/> representing a try block with a finally block and no catch statements.
136         /// </summary>
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);
142         }
143
144         /// <summary>
145         /// Creates a <see cref="TryExpression"/> representing a try block with any number of catch statements and neither a fault nor finally block.
146         /// </summary>
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);
152         }
153
154         /// <summary>
155         /// Creates a <see cref="TryExpression"/> representing a try block with any number of catch statements and a finally block.
156         /// </summary>
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);
163         }
164
165         /// <summary>
166         /// Creates a <see cref="TryExpression"/> representing a try block with the specified elements.
167         /// </summary>
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");
176
177             var @catch = handlers.ToReadOnly();
178             ContractUtils.RequiresNotNullItems(@catch, "handlers");
179             ValidateTryAndCatchHaveSameType(type, body, @catch);
180
181             if (fault != null) {
182                 if (@finally != null || @catch.Count > 0) {
183                     throw Error.FaultCannotHaveCatchOrFinally();
184                 }
185                 RequiresCanRead(fault, "fault");
186             } else if (@finally != null) {
187                 RequiresCanRead(@finally, "finally");
188             } else if (@catch.Count == 0) {
189                 throw Error.TryMustHaveCatchFinallyOrFault();
190             }
191
192             return new TryExpression(type ?? body.Type, body, @finally, fault, @catch);
193         }
194
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"
198             if (type != null) {
199                 if (type != typeof(void)) {
200                     if (!TypeUtils.AreReferenceAssignable(type, tryBody.Type)) {
201                         throw Error.ArgumentTypesMustMatch();
202                     }
203                     foreach (var cb in handlers) {
204                         if (!TypeUtils.AreReferenceAssignable(type, cb.Body.Type)) {
205                             throw Error.ArgumentTypesMustMatch();
206                         }
207                     }
208                 }
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();
214                     }
215                 }
216             } else {
217                 //Body of every catch must have the same type of body of try.
218                 type = tryBody.Type;
219                 foreach (CatchBlock cb in handlers) {
220                     if (cb.Body == null || !TypeUtils.AreEquivalent(cb.Body.Type, type)) {
221                         throw Error.BodyOfCatchMustHaveSameTypeAsBodyOfTry();
222                     }
223                 }
224             }
225         }
226     }
227
228 }