2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Ast / NewArrayExpression.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 using System.Runtime.CompilerServices;
27 #if !CODEPLEX_40
28 using Microsoft.Runtime.CompilerServices;
29 #endif
30
31
32 #if CODEPLEX_40
33 namespace System.Linq.Expressions {
34 #else
35 namespace Microsoft.Linq.Expressions {
36 #endif
37
38     /// <summary>
39     /// Represents creating a new array and possibly initializing the elements of the new array.
40     /// </summary>
41 #if !SILVERLIGHT
42     [DebuggerTypeProxy(typeof(Expression.NewArrayExpressionProxy))]
43 #endif
44     public class NewArrayExpression : Expression {
45         private readonly ReadOnlyCollection<Expression> _expressions;
46         private readonly Type _type;
47
48         internal NewArrayExpression(Type type, ReadOnlyCollection<Expression> expressions) {
49             _expressions = expressions;
50             _type = type;
51         }
52
53         internal static NewArrayExpression Make(ExpressionType nodeType, Type type, ReadOnlyCollection<Expression> expressions) {
54             if (nodeType == ExpressionType.NewArrayInit) {
55                 return new NewArrayInitExpression(type, expressions);
56             } else {
57                 return new NewArrayBoundsExpression(type, expressions);
58             }
59         }
60
61         /// <summary>
62         /// Gets the static type of the expression that this <see cref="Expression" /> represents. (Inherited from <see cref="Expression"/>.)
63         /// </summary>
64         /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
65         public sealed override Type Type {
66             get { return _type; }
67         }
68
69         /// <summary>
70         /// Gets the bounds of the array if the value of the <see cref="P:NodeType"/> property is NewArrayBounds, or the values to initialize the elements of the new array if the value of the <see cref="P:NodeType"/> property is NewArrayInit. 
71         /// </summary>
72         public ReadOnlyCollection<Expression> Expressions {
73             get { return _expressions; }
74         }
75
76         internal override Expression Accept(ExpressionVisitor visitor) {
77             return visitor.VisitNewArray(this);
78         }
79     }
80
81     internal sealed class NewArrayInitExpression : NewArrayExpression {
82         internal NewArrayInitExpression(Type type, ReadOnlyCollection<Expression> expressions)
83             : base(type, expressions) {
84         }
85
86
87         /// <summary>
88         /// Returns the node type of this <see cref="Expression" />. (Inherited from <see cref="Expression" />.)
89         /// </summary>
90         /// <returns>The <see cref="ExpressionType"/> that represents this expression.</returns>
91         public sealed override ExpressionType NodeType {
92             get { return ExpressionType.NewArrayInit; }
93         }
94     }
95
96     internal sealed class NewArrayBoundsExpression : NewArrayExpression {
97         internal NewArrayBoundsExpression(Type type, ReadOnlyCollection<Expression> expressions)
98             : base(type, expressions) {
99         }
100
101         /// <summary>
102         /// Returns the node type of this <see cref="Expression" />. (Inherited from <see cref="Expression" />.)
103         /// </summary>
104         /// <returns>The <see cref="ExpressionType"/> that represents this expression.</returns>
105         public sealed override ExpressionType NodeType {
106             get { return ExpressionType.NewArrayBounds; }
107         }
108     }
109     
110     public partial class Expression {
111
112         #region NewArrayInit
113
114
115         /// <summary>
116         /// Creates a new array expression of the specified type from the provided initializers.
117         /// </summary>
118         /// <param name="type">A Type that represents the element type of the array.</param>
119         /// <param name="initializers">The expressions used to create the array elements.</param>
120         /// <returns>An instance of the <see cref="NewArrayExpression"/>.</returns>
121         public static NewArrayExpression NewArrayInit(Type type, params Expression[] initializers) {
122             return NewArrayInit(type, (IEnumerable<Expression>)initializers);
123         }
124
125         /// <summary>
126         /// Creates a new array expression of the specified type from the provided initializers.
127         /// </summary>
128         /// <param name="type">A Type that represents the element type of the array.</param>
129         /// <param name="initializers">The expressions used to create the array elements.</param>
130         /// <returns>An instance of the <see cref="NewArrayExpression"/>.</returns>
131         public static NewArrayExpression NewArrayInit(Type type, IEnumerable<Expression> initializers) {
132             ContractUtils.RequiresNotNull(type, "type");
133             ContractUtils.RequiresNotNull(initializers, "initializers");
134             if (type.Equals(typeof(void))) {
135                 throw Error.ArgumentCannotBeOfTypeVoid();
136             }
137
138             ReadOnlyCollection<Expression> initializerList = initializers.ToReadOnly();
139
140             Expression[] newList = null;
141             for (int i = 0, n = initializerList.Count; i < n; i++) {
142                 Expression expr = initializerList[i];
143                 RequiresCanRead(expr, "initializers");
144
145                 if (!TypeUtils.AreReferenceAssignable(type, expr.Type)) {
146                     if (TypeUtils.IsSameOrSubclass(typeof(LambdaExpression), type) && type.IsAssignableFrom(expr.GetType())) {
147                         expr = Expression.Quote(expr);
148                     } else {
149                         throw Error.ExpressionTypeCannotInitializeArrayType(expr.Type, type);
150                     }
151                     if (newList == null) {
152                         newList = new Expression[initializerList.Count];
153                         for (int j = 0; j < i; j++) {
154                             newList[j] = initializerList[j];
155                         }
156                     }
157                 }
158                 if (newList != null) {
159                     newList[i] = expr;
160                 }
161             }
162             if (newList != null) {
163                 initializerList = new TrueReadOnlyCollection<Expression>(newList);
164             }
165
166             return NewArrayExpression.Make(ExpressionType.NewArrayInit, type.MakeArrayType(), initializerList);
167         }
168
169         #endregion
170
171         #region NewArrayBounds
172
173
174         /// <summary>
175         /// Creates a <see cref="NewArrayExpression"/> that represents creating an array that has a specified rank. 
176         /// </summary>
177         /// <param name="type">A <see cref="Type"/> that represents the element type of the array.</param>
178         /// <param name="bounds">An array that contains Expression objects to use to populate the Expressions collection.</param>
179         /// <returns>A <see cref="NewArrayExpression"/> that has the <see cref="P:NodeType"/> property equal to type and the <see cref="P:Expressions"/> property set to the specified value.</returns>
180         public static NewArrayExpression NewArrayBounds(Type type, params Expression[] bounds) {
181             return NewArrayBounds(type, (IEnumerable<Expression>)bounds);
182         }
183
184
185         /// <summary>
186         /// Creates a <see cref="NewArrayExpression"/> that represents creating an array that has a specified rank. 
187         /// </summary>
188         /// <param name="type">A <see cref="Type"/> that represents the element type of the array.</param>
189         /// <param name="bounds">An IEnumerable{T} that contains Expression objects to use to populate the Expressions collection.</param>
190         /// <returns>A <see cref="NewArrayExpression"/> that has the <see cref="P:NodeType"/> property equal to type and the <see cref="P:Expressions"/> property set to the specified value.</returns>
191         public static NewArrayExpression NewArrayBounds(Type type, IEnumerable<Expression> bounds) {
192             ContractUtils.RequiresNotNull(type, "type");
193             ContractUtils.RequiresNotNull(bounds, "bounds");
194
195             if (type.Equals(typeof(void))) {
196                 throw Error.ArgumentCannotBeOfTypeVoid();
197             }
198
199             ReadOnlyCollection<Expression> boundsList = bounds.ToReadOnly();
200
201             int dimensions = boundsList.Count;
202             ContractUtils.Requires(dimensions > 0, "bounds", Strings.BoundsCannotBeLessThanOne);
203
204             for (int i = 0; i < dimensions; i++) {
205                 Expression expr = boundsList[i];
206                 RequiresCanRead(expr, "bounds");
207                 if (!TypeUtils.IsInteger(expr.Type)) {
208                     throw Error.ArgumentMustBeInteger();
209                 }
210             }
211
212             Type arrayType;
213             if (dimensions == 1) {
214                 //To get a vector, need call Type.MakeArrayType(). 
215                 //Type.MakeArrayType(1) gives a non-vector array, which will cause type check error.
216                 arrayType = type.MakeArrayType();
217             } else {
218                 arrayType = type.MakeArrayType(dimensions);
219             }
220
221             return NewArrayExpression.Make(ExpressionType.NewArrayBounds, arrayType, bounds.ToReadOnly());
222         }
223
224         #endregion
225
226     }
227 }