PageRenderTime 26ms CodeModel.GetById 12ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

/IronPython_2_0/Src/Microsoft.Scripting.Core/Ast/IndexExpression.cs

#
C# | 284 lines | 201 code | 48 blank | 35 comment | 63 complexity | 3969e8e98ea827eb80a910376f2c2f96 MD5 | raw file
  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 * ***************************************************************************/
 15using System; using Microsoft;
 16
 17
 18using System.Collections.Generic;
 19using System.Collections.ObjectModel;
 20using System.Diagnostics;
 21using System.Reflection;
 22using Microsoft.Scripting.Actions;
 23using Microsoft.Scripting.Utils;
 24using System.Text;
 25
 26namespace Microsoft.Linq.Expressions {
 27    /// <summary>
 28    /// Represents property or array indexing
 29    /// </summary>
 30    public sealed class IndexExpression : Expression {
 31        private readonly Expression _instance;
 32        private readonly PropertyInfo _indexer;
 33        private readonly ReadOnlyCollection<Expression> _arguments;
 34
 35        internal IndexExpression(
 36            Expression instance,
 37            PropertyInfo indexer,
 38            Annotations annotations,
 39            ReadOnlyCollection<Expression> arguments,
 40            Type type,
 41            bool canRead,
 42            bool canWrite)
 43            : base(ExpressionType.Index, type, false, annotations, canRead, canWrite) {
 44
 45            if (indexer == null) {
 46                Debug.Assert(instance != null && instance.Type.IsArray);
 47                Debug.Assert(instance.Type.GetArrayRank() == arguments.Count);
 48                Debug.Assert(instance.Type.GetElementType() == type);
 49            }
 50
 51            _instance = instance;
 52            _indexer = indexer;
 53            _arguments = arguments;
 54        }
 55
 56        public Expression Object {
 57            get { return _instance; }
 58        }
 59
 60        /// <summary>
 61        /// If this is an indexed property, returns the property
 62        /// If this is an array indexing operation, returns null
 63        /// </summary>
 64        public PropertyInfo Indexer {
 65            get { return _indexer; }
 66        }
 67
 68        public ReadOnlyCollection<Expression> Arguments {
 69            get { return _arguments; }
 70        }
 71
 72        internal override void BuildString(StringBuilder builder) {            
 73            Debug.Assert(builder != null);
 74
 75            if (_instance != null) {
 76                _instance.BuildString(builder);
 77            } else {
 78                Debug.Assert(_indexer != null);
 79                builder.Append(_indexer.DeclaringType.Name);
 80            }
 81
 82            if (_indexer != null) {
 83                builder.Append(".");
 84                builder.Append(_indexer.Name);
 85            }
 86            builder.Append("[");
 87            for (int i = 0, n = _arguments.Count; i < n; i++) {
 88                if (i > 0) {
 89                    builder.Append(", ");
 90                }
 91                _arguments[i].BuildString(builder);
 92            }
 93            builder.Append("]");
 94        }
 95
 96        internal override Expression Accept(ExpressionTreeVisitor visitor) {
 97            return visitor.VisitIndex(this);
 98        }
 99    }
100
101
102    /// <summary>
103    /// Factory methods.
104    /// </summary>
105    public partial class Expression {
106
107        public static IndexExpression MakeIndex(Expression instance, PropertyInfo indexer, Annotations annotations, IEnumerable<Expression> arguments) {
108            if (indexer != null) {
109                return Property(instance, indexer, annotations, arguments);
110            } else {
111                return ArrayAccess(instance, annotations, arguments);
112            }
113        }
114
115        #region ArrayAccess
116
117        public static IndexExpression ArrayAccess(Expression array, params Expression[] indexes) {
118            return ArrayAccess(array, null, (IEnumerable<Expression>)indexes);
119        }
120
121        public static IndexExpression ArrayAccess(Expression array, IEnumerable<Expression> indexes) {
122            return ArrayAccess(array, null, indexes);
123        }
124
125        public static IndexExpression ArrayAccess(Expression array, Annotations annotations, params Expression[] indexes) {
126            return ArrayAccess(array, annotations, (IEnumerable<Expression>)indexes);
127        }
128
129        public static IndexExpression ArrayAccess(Expression array, Annotations annotations, IEnumerable<Expression> indexes) {
130            RequiresCanRead(array, "array");
131
132            Type arrayType = array.Type;
133            if (!arrayType.IsArray) {
134                throw Error.ArgumentMustBeArray();
135            }
136
137            var indexList = indexes.ToReadOnly();
138            if (arrayType.GetArrayRank() != indexList.Count) {
139                throw Error.IncorrectNumberOfIndexes();
140            }
141
142            foreach (Expression e in indexList) {
143                RequiresCanRead(e, "indexes");
144                if (e.Type != typeof(int)) {
145                    throw Error.ArgumentMustBeArrayIndexType();
146                }
147            }
148
149            return new IndexExpression(array, null, annotations, indexList, arrayType.GetElementType(), true, true);
150        }
151
152        #endregion
153
154        #region Property
155
156        public static IndexExpression Property(Expression instance, PropertyInfo indexer, params Expression[] arguments) {
157            return Property(instance, indexer, null, (IEnumerable<Expression>)arguments);
158        }
159        
160        public static IndexExpression Property(Expression instance, PropertyInfo indexer, IEnumerable<Expression> arguments) {
161            return Property(instance, indexer, null, arguments);
162        }
163
164        public static IndexExpression Property(Expression instance, PropertyInfo indexer, Annotations annotations, params Expression[] arguments) {
165            return Property(instance, indexer, annotations, (IEnumerable<Expression>)arguments);
166        }
167
168        public static IndexExpression Property(Expression instance, PropertyInfo indexer, Annotations annotations, IEnumerable<Expression> arguments) {
169            var argList = arguments.ToReadOnly();
170            ValidateIndexedProperty(instance, indexer, ref argList);
171            return new IndexExpression(instance, indexer, annotations, argList, indexer.PropertyType, indexer.CanRead, indexer.CanWrite);
172        }
173
174        // CTS places no restrictions on properties (see ECMA-335 8.11.3),
175        // so we validate that the property conforms to CLS rules here.
176        //
177        // TODO: Do we still need all of this now that we take PropertyInfo?
178        // Does reflection help us out at all? Expression.Property skips all of
179        // these checks, so either it needs more checks or we need less here.
180        private static void ValidateIndexedProperty(Expression instance, PropertyInfo property, ref ReadOnlyCollection<Expression> argList) {
181
182            // If both getter and setter specified, all their parameter types
183            // should match, with exception of the last setter parameter which
184            // should match the type returned by the get method.
185            // Accessor parameters cannot be ByRef.
186
187            ContractUtils.RequiresNotNull(property, "property");
188            ContractUtils.Requires(!property.PropertyType.IsByRef, "property", Strings.PropertyCannotHaveRefType);
189            ContractUtils.Requires(property.PropertyType != typeof(void), "property", Strings.PropertyTypeCannotBeVoid);
190
191            ParameterInfo[] getParameters = null;
192            MethodInfo getter = property.GetGetMethod(true);
193            if (getter != null) {
194                getParameters = getter.GetParametersCached();
195                ValidateAccessor(instance, getter, getParameters, ref argList);
196            }
197
198            MethodInfo setter = property.GetSetMethod(true);
199            if (setter != null) {
200                ParameterInfo[] setParameters = setter.GetParametersCached();
201                ContractUtils.Requires(setParameters.Length > 0, "property", Strings.SetterHasNoParams);
202
203                // valueType is the type of the value passed to the setter (last parameter)
204                Type valueType = setParameters[setParameters.Length - 1].ParameterType;
205                ContractUtils.Requires(!valueType.IsByRef, "property", Strings.PropertyCannotHaveRefType);
206                ContractUtils.Requires(setter.ReturnType == typeof(void), "property", Strings.SetterMustBeVoid);
207                ContractUtils.Requires(property.PropertyType == valueType, "property", Strings.PropertyTyepMustMatchSetter);
208
209                if (getter != null) {
210                    ContractUtils.Requires(!(getter.IsStatic ^ setter.IsStatic), "property", Strings.BothAccessorsMustBeStatic);
211                    ContractUtils.Requires(getParameters.Length == setParameters.Length - 1, "property", Strings.IndexesOfSetGetMustMatch);
212
213                    for (int i = 0; i < getParameters.Length; i++) {
214                        ContractUtils.Requires(getParameters[i].ParameterType == setParameters[i].ParameterType, "property", Strings.IndexesOfSetGetMustMatch);
215                    }
216                } else {
217                    ValidateAccessor(instance, setter, setParameters.RemoveLast(), ref argList);
218                }
219            }
220
221            if (getter == null && setter == null) {
222                throw Error.PropertyDoesNotHaveAccessor(property);
223            }
224        }
225
226        private static void ValidateAccessor(Expression instance, MethodInfo method, ParameterInfo[] indexes, ref ReadOnlyCollection<Expression> arguments) {
227            ContractUtils.RequiresNotNull(arguments, "arguments");
228
229            ValidateMethodInfo(method);
230            ContractUtils.Requires((method.CallingConvention & CallingConventions.VarArgs) == 0, "method", Strings.AccessorsCannotHaveVarArgs);            
231            if (method.IsStatic) {
232                ContractUtils.Requires(instance == null, "instance", Strings.OnlyStaticMethodsHaveNullExpr); 
233            } else {
234                RequiresCanRead(instance, "instance");
235                ValidateCallInstanceType(instance.Type, method);
236            }
237
238            ValidateAccessorArgumentTypes(method, indexes, ref arguments);
239        }
240
241        private static void ValidateAccessorArgumentTypes(MethodInfo method, ParameterInfo[] indexes, ref ReadOnlyCollection<Expression> arguments) {
242            if (indexes.Length > 0) {
243                if (indexes.Length != arguments.Count) {
244                    throw Error.IncorrectNumberOfMethodCallArguments(method);
245                }
246                Expression[] newArgs = null;
247                for (int i = 0, n = indexes.Length; i < n; i++) {
248                    Expression arg = arguments[i];
249                    ParameterInfo pi = indexes[i];
250                    RequiresCanRead(arg, "arguments");
251
252                    Type pType = pi.ParameterType;
253                    ContractUtils.Requires(!pType.IsByRef, "indexes", Strings.AccessorsCannotHaveByRefArgs);
254                    TypeUtils.ValidateType(pType);
255
256                    if (!TypeUtils.AreReferenceAssignable(pType, arg.Type)) {
257                        if (TypeUtils.IsSameOrSubclass(typeof(Expression), pType) && TypeUtils.AreAssignable(pType, arg.GetType())) {
258                            arg = Expression.Quote(arg);
259                        } else {
260                            throw Error.ExpressionTypeDoesNotMatchMethodParameter(arg.Type, pType, method);
261                        }
262                    }
263                    if (newArgs == null && arg != arguments[i]) {
264                        newArgs = new Expression[arguments.Count];
265                        for (int j = 0; j < i; j++) {
266                            newArgs[j] = arguments[j];
267                        }
268                    }
269                    if (newArgs != null) {
270                        newArgs[i] = arg;
271                    }
272                }
273                if (newArgs != null) {
274                    arguments = new ReadOnlyCollection<Expression>(newArgs);
275                }
276
277            } else if (arguments.Count > 0) {
278                throw Error.IncorrectNumberOfMethodCallArguments(method);
279            }
280        }
281
282        #endregion
283    }
284}