PageRenderTime 55ms CodeModel.GetById 30ms app.highlight 18ms RepoModel.GetById 2ms app.codeStats 0ms

/IronPython_Main/Languages/IronPython/IronPython/Runtime/Types/ReflectedProperty.cs

#
C# | 284 lines | 211 code | 45 blank | 28 comment | 43 complexity | 47dad958c6003d024df157e16fb6e067 MD5 | raw file
  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#if !CLR2
 17using System.Linq.Expressions;
 18#else
 19using Microsoft.Scripting.Ast;
 20#endif
 21
 22using System;
 23using System.Diagnostics;
 24using System.Dynamic;
 25using System.Reflection;
 26using System.Runtime.CompilerServices;
 27
 28using Microsoft.Scripting;
 29using Microsoft.Scripting.Actions;
 30using Microsoft.Scripting.Generation;
 31using Microsoft.Scripting.Utils;
 32
 33using IronPython.Runtime.Binding;
 34using IronPython.Runtime.Operations;
 35
 36using AstUtils = Microsoft.Scripting.Ast.Utils;
 37
 38namespace IronPython.Runtime.Types {
 39    [PythonType("getset_descriptor")]
 40    public class ReflectedProperty : ReflectedGetterSetter, ICodeFormattable {
 41        private readonly PropertyInfo/*!*/ _info;
 42
 43        public ReflectedProperty(PropertyInfo info, MethodInfo getter, MethodInfo setter, NameType nt)
 44            : base(new MethodInfo[] { getter }, new MethodInfo[] { setter }, nt) {
 45            Debug.Assert(info != null);
 46
 47            _info = info;
 48        }
 49
 50        /// <summary>
 51        /// True if generating code for gets can result in more optimal accesses.
 52        /// </summary>
 53        internal override bool CanOptimizeGets {
 54            get {
 55                return true;
 56            }
 57        }
 58
 59        public ReflectedProperty(PropertyInfo info, MethodInfo[] getters, MethodInfo[] setters, NameType nt)
 60            : base(getters, setters, nt) {
 61            Debug.Assert(info != null);
 62
 63            _info = info;
 64        }
 65
 66        internal override bool TrySetValue(CodeContext context, object instance, PythonType owner, object value) {
 67            if (Setter.Length == 0) {
 68                return false;
 69            }
 70
 71            if (instance == null) {
 72                foreach (MethodInfo mi in Setter) {
 73                    if(mi.IsStatic && DeclaringType != owner.UnderlyingSystemType) {
 74                        return false;
 75                    } else if (mi.IsProtected()) {
 76                        throw PythonOps.TypeErrorForProtectedMember(owner.UnderlyingSystemType, _info.Name);
 77                    }
 78                }
 79            } else if (instance != null) {
 80                foreach (MethodInfo mi in Setter) {
 81                    if (mi.IsStatic) {
 82                        return false;
 83                    }
 84                }
 85            }
 86
 87            return CallSetter(context, PythonContext.GetContext(context).GetGenericCallSiteStorage(), instance, ArrayUtils.EmptyObjects, value);
 88        }
 89
 90        internal override Type DeclaringType {
 91            get { return _info.DeclaringType; }
 92        }
 93
 94        public override string __name__ {
 95            get { return _info.Name; }
 96        }
 97
 98        public PropertyInfo Info {
 99            [PythonHidden]
100            get {
101                return _info;
102            }
103        }
104
105        public override PythonType PropertyType {
106            [PythonHidden]
107            get {
108                return DynamicHelpers.GetPythonTypeFromType(_info.PropertyType);
109            }
110        }
111
112        internal override bool TryGetValue(CodeContext context, object instance, PythonType owner, out object value) {
113            PerfTrack.NoteEvent(PerfTrack.Categories.Properties, this);
114
115            value = CallGetter(context, owner, PythonContext.GetContext(context).GetGenericCallSiteStorage0(), instance);
116            return true;
117        }
118
119        private object CallGetter(CodeContext context, PythonType owner, SiteLocalStorage<CallSite<Func<CallSite, CodeContext, object, object>>> storage, object instance) {
120            if (NeedToReturnProperty(instance, Getter)) {
121                return this;
122            }
123
124            if (Getter.Length == 0) {
125                throw new MissingMemberException("unreadable property");
126            }
127
128            if (owner == null) {
129                owner = DynamicHelpers.GetPythonType(instance);
130            }
131
132            // this matches the logic in the default binder when it does a property get.  We 
133            // need to duplicate it here to be consistent for all gets.
134            MethodInfo[] members = Getter;
135
136            Type type = owner.UnderlyingSystemType;
137            if (Getter.Length > 1) {
138                // if we were given multiple members pick the member closest to the type...                
139                Type bestMemberDeclaringType = Getter[0].DeclaringType;
140                MethodInfo bestMember = Getter[0];
141
142                for (int i = 1; i < Getter.Length; i++) {
143                    MethodInfo mt = Getter[i];
144                    if (!IsApplicableForType(type, mt)) {
145                        continue;
146                    }
147
148                    if (Getter[i].DeclaringType.IsSubclassOf(bestMemberDeclaringType) ||
149                        !IsApplicableForType(type, bestMember)) {
150                        bestMember = Getter[i];
151                        bestMemberDeclaringType = Getter[i].DeclaringType;
152                    }
153                }
154                members = new MethodInfo[] { bestMember };
155            }
156
157            BuiltinFunction target = PythonTypeOps.GetBuiltinFunction(type, __name__, members);
158
159            return target.Call0(context, storage, instance);
160        }
161
162        private static bool IsApplicableForType(Type type, MethodInfo mt) {
163            return mt.DeclaringType == type || type.IsSubclassOf(mt.DeclaringType);
164        }
165
166        internal override bool GetAlwaysSucceeds {
167            get {
168                return true;
169            }
170        }
171
172        internal override bool TryDeleteValue(CodeContext context, object instance, PythonType owner) {
173            __delete__(instance);
174            return true;
175        }
176
177        internal override void MakeGetExpression(PythonBinder/*!*/ binder, Expression/*!*/ codeContext, DynamicMetaObject instance, DynamicMetaObject/*!*/ owner, ConditionalBuilder/*!*/ builder) {
178            if (Getter.Length != 0 && !Getter[0].IsPublic) {
179                // fallback to runtime call
180                base.MakeGetExpression(binder, codeContext, instance, owner, builder);
181            } else if (NeedToReturnProperty(instance, Getter)) {
182                builder.FinishCondition(AstUtils.Constant(this));
183            } else if (Getter[0].ContainsGenericParameters) {
184                builder.FinishCondition(
185                    DefaultBinder.MakeError(
186                        binder.MakeContainsGenericParametersError(
187                            MemberTracker.FromMemberInfo(_info)
188                        ),
189                        typeof(object)
190                    ).Expression
191                );
192            } else if (instance != null) {
193                builder.FinishCondition(
194                    AstUtils.Convert(
195                        binder.MakeCallExpression(
196                            new PythonOverloadResolverFactory(binder, codeContext),
197                            Getter[0],
198                            instance
199                        ).Expression,
200                        typeof(object)
201                    )
202                );
203            } else {
204                builder.FinishCondition(
205                    AstUtils.Convert(
206                        binder.MakeCallExpression(
207                            new PythonOverloadResolverFactory(binder, codeContext),
208                            Getter[0]
209                        ).Expression,
210                        typeof(object)
211                    )
212                );                
213            }
214        }
215
216        internal override bool IsAlwaysVisible {
217            get {
218                return NameType == NameType.PythonProperty;
219            }
220        }
221
222        internal override bool IsSetDescriptor(CodeContext context, PythonType owner) {
223            return Setter.Length != 0;
224        }
225
226        #region Public Python APIs
227
228        /// <summary>
229        /// Convenience function for users to call directly
230        /// </summary>
231        [PythonHidden]
232        public object GetValue(CodeContext context, object instance) {
233            object value;
234            if (TryGetValue(context, instance, DynamicHelpers.GetPythonType(instance), out value)) {
235                return value;
236            }
237            throw new InvalidOperationException("cannot get property");
238        }
239
240        /// <summary>
241        /// Convenience function for users to call directly
242        /// </summary>
243        [PythonHidden]
244        public void SetValue(CodeContext context, object instance, object value) {
245            if (!TrySetValue(context, instance, DynamicHelpers.GetPythonType(instance), value)) {
246                throw new InvalidOperationException("cannot set property");
247            }
248        }
249
250        public void __set__(CodeContext context, object instance, object value) {
251            // TODO: Throw?  currently we have a test that verifies we never throw when this is called directly.
252            TrySetValue(context, instance, DynamicHelpers.GetPythonType(instance), value);
253        }
254
255        public void __delete__(object instance) {
256            if (Setter.Length != 0)
257                throw PythonOps.AttributeErrorForReadonlyAttribute(
258                    DynamicHelpers.GetPythonTypeFromType(DeclaringType).Name,
259                    __name__);
260            else
261                throw PythonOps.AttributeErrorForBuiltinAttributeDeletion(
262                    DynamicHelpers.GetPythonTypeFromType(DeclaringType).Name,
263                    __name__);
264        }
265
266        public string __doc__ {
267            get {
268                return DocBuilder.DocOneInfo(Info);
269            }
270        }
271
272        #endregion
273
274        #region ICodeFormattable Members
275
276        public string/*!*/ __repr__(CodeContext/*!*/ context) {
277            return string.Format("<property# {0} on {1}>",
278                __name__,
279                DynamicHelpers.GetPythonTypeFromType(DeclaringType).Name);
280        }
281
282        #endregion
283    }
284}