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