PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/DLR_Main/Languages/IronPython/IronPython/Runtime/Types/ReflectedField.cs

https://bitbucket.org/mdavid/dlr
C# | 244 lines | 172 code | 40 blank | 32 comment | 40 complexity | 4a3cce1702b57bdbc66bbbf20d9a7648 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. #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 IronPython.Runtime.Binding;
  28. using IronPython.Runtime.Exceptions;
  29. using IronPython.Runtime.Operations;
  30. namespace IronPython.Runtime.Types {
  31. using Ast = Expression;
  32. using AstUtils = Microsoft.Scripting.Ast.Utils;
  33. [PythonType("field#")]
  34. public sealed class ReflectedField : PythonTypeSlot, ICodeFormattable {
  35. private readonly NameType _nameType;
  36. internal readonly FieldInfo/*!*/ _info;
  37. internal const string UpdateValueTypeFieldWarning = "Setting field {0} on value type {1} may result in updating a copy. Use {1}.{0}.SetValue(instance, value) if this is safe. For more information help({1}.{0}.SetValue).";
  38. public ReflectedField(FieldInfo/*!*/ info, NameType nameType) {
  39. Debug.Assert(info != null);
  40. this._nameType = nameType;
  41. this._info = info;
  42. }
  43. public ReflectedField(FieldInfo/*!*/ info)
  44. : this(info, NameType.PythonField) {
  45. }
  46. #region Public Python APIs
  47. public FieldInfo Info {
  48. [PythonHidden]
  49. get {
  50. return _info;
  51. }
  52. }
  53. /// <summary>
  54. /// Convenience function for users to call directly
  55. /// </summary>
  56. public object GetValue(CodeContext context, object instance) {
  57. object value;
  58. if (TryGetValue(context, instance, DynamicHelpers.GetPythonType(instance), out value)) {
  59. return value;
  60. }
  61. throw new InvalidOperationException("cannot get field");
  62. }
  63. /// <summary>
  64. /// This function can be used to set a field on a value type without emitting a warning. Otherwise it is provided only to have symmetry with properties which have GetValue/SetValue for supporting explicitly implemented interfaces.
  65. ///
  66. /// Setting fields on value types usually warns because it can silently fail to update the value you expect. For example consider this example where Point is a value type with the public fields X and Y:
  67. ///
  68. /// arr = System.Array.CreateInstance(Point, 10)
  69. /// arr[0].X = 42
  70. /// print arr[0].X
  71. ///
  72. /// prints 0. This is because reading the value from the array creates a copy of the value. Setting the value then mutates the copy and the array does not get updated. The same problem exists when accessing members of a class.
  73. /// </summary>
  74. public void SetValue(CodeContext context, object instance, object value) {
  75. if (!TrySetValueWorker(context, instance, DynamicHelpers.GetPythonType(instance), value, true)) {
  76. throw new InvalidOperationException("cannot set field");
  77. }
  78. }
  79. public void __set__(CodeContext/*!*/ context, object instance, object value) {
  80. if (instance == null && _info.IsStatic) {
  81. DoSet(context, null, value, false);
  82. } else if (!_info.IsStatic) {
  83. DoSet(context, instance, value, false);
  84. } else {
  85. throw PythonOps.AttributeErrorForReadonlyAttribute(_info.DeclaringType.Name, _info.Name);
  86. }
  87. }
  88. [SpecialName]
  89. public void __delete__(object instance) {
  90. throw PythonOps.AttributeErrorForBuiltinAttributeDeletion(_info.DeclaringType.Name, _info.Name);
  91. }
  92. public string __doc__ {
  93. get {
  94. return DocBuilder.DocOneInfo(_info);
  95. }
  96. }
  97. public PythonType FieldType {
  98. [PythonHidden]
  99. get {
  100. return DynamicHelpers.GetPythonTypeFromType(_info.FieldType);
  101. }
  102. }
  103. #endregion
  104. #region Internal APIs
  105. internal override bool TryGetValue(CodeContext context, object instance, PythonType owner, out object value) {
  106. PerfTrack.NoteEvent(PerfTrack.Categories.Fields, this);
  107. if (instance == null) {
  108. if (_info.IsStatic) {
  109. value = _info.GetValue(null);
  110. } else {
  111. value = this;
  112. }
  113. } else {
  114. value = _info.GetValue(context.LanguageContext.Binder.Convert(instance, _info.DeclaringType));
  115. }
  116. return true;
  117. }
  118. internal override bool GetAlwaysSucceeds {
  119. get {
  120. return true;
  121. }
  122. }
  123. internal override bool CanOptimizeGets {
  124. get {
  125. return !_info.IsLiteral;
  126. }
  127. }
  128. internal override bool TrySetValue(CodeContext context, object instance, PythonType owner, object value) {
  129. return TrySetValueWorker(context, instance, owner, value, false);
  130. }
  131. private bool TrySetValueWorker(CodeContext context, object instance, PythonType owner, object value, bool suppressWarning) {
  132. if (ShouldSetOrDelete(owner)) {
  133. DoSet(context, instance, value, suppressWarning);
  134. return true;
  135. }
  136. return false;
  137. }
  138. internal override bool IsSetDescriptor(CodeContext context, PythonType owner) {
  139. // field is settable if it is not readonly
  140. return (_info.Attributes & FieldAttributes.InitOnly) == 0 && !_info.IsLiteral;
  141. }
  142. internal override bool TryDeleteValue(CodeContext context, object instance, PythonType owner) {
  143. if (ShouldSetOrDelete(owner)) {
  144. throw PythonOps.AttributeErrorForBuiltinAttributeDeletion(_info.DeclaringType.Name, _info.Name);
  145. }
  146. return false;
  147. }
  148. internal override bool IsAlwaysVisible {
  149. get {
  150. return _nameType == NameType.PythonField;
  151. }
  152. }
  153. internal override void MakeGetExpression(PythonBinder/*!*/ binder, Expression/*!*/ codeContext, DynamicMetaObject instance, DynamicMetaObject/*!*/ owner, ConditionalBuilder/*!*/ builder) {
  154. if (!_info.IsPublic || _info.DeclaringType.ContainsGenericParameters) {
  155. // fallback to reflection
  156. base.MakeGetExpression(binder, codeContext, instance, owner, builder);
  157. } else if (instance == null) {
  158. if (_info.IsStatic) {
  159. builder.FinishCondition(AstUtils.Convert(Ast.Field(null, _info), typeof(object)));
  160. } else {
  161. builder.FinishCondition(Ast.Constant(this));
  162. }
  163. } else {
  164. builder.FinishCondition(
  165. AstUtils.Convert(
  166. Ast.Field(
  167. binder.ConvertExpression(
  168. instance.Expression,
  169. _info.DeclaringType,
  170. ConversionResultKind.ExplicitCast,
  171. new PythonOverloadResolverFactory(binder, codeContext)
  172. ),
  173. _info
  174. ),
  175. typeof(object)
  176. )
  177. );
  178. }
  179. }
  180. #endregion
  181. #region Private helpers
  182. private void DoSet(CodeContext context, object instance, object val, bool suppressWarning) {
  183. PerfTrack.NoteEvent(PerfTrack.Categories.Fields, this);
  184. if (_info.IsInitOnly || _info.IsLiteral) {
  185. throw PythonOps.AttributeErrorForReadonlyAttribute(_info.DeclaringType.Name, _info.Name);
  186. } else if (!suppressWarning && instance != null && instance.GetType().IsValueType) {
  187. PythonOps.Warn(context, PythonExceptions.RuntimeWarning, UpdateValueTypeFieldWarning, _info.Name, _info.DeclaringType.Name);
  188. }
  189. _info.SetValue(instance, context.LanguageContext.Binder.Convert(val, _info.FieldType));
  190. }
  191. private bool ShouldSetOrDelete(PythonType type) {
  192. PythonType dt = type as PythonType;
  193. // statics must be assigned through their type, not a derived type. Non-statics can
  194. // be assigned through their instances.
  195. return (dt != null && _info.DeclaringType == dt.UnderlyingSystemType) || !_info.IsStatic || _info.IsLiteral || _info.IsInitOnly;
  196. }
  197. #endregion
  198. #region ICodeFormattable Members
  199. public string/*!*/ __repr__(CodeContext/*!*/ context) {
  200. return string.Format("<field# {0} on {1}>", _info.Name, _info.DeclaringType.Name);
  201. }
  202. #endregion
  203. }
  204. }