PageRenderTime 62ms CodeModel.GetById 34ms RepoModel.GetById 0ms app.codeStats 1ms

/Merlin/Main/Languages/IronPython/IronPython/Runtime/Operations/UserTypeOps.cs

https://github.com/PascalN2/ironruby
C# | 432 lines | 331 code | 67 blank | 34 comment | 103 complexity | 600573d271b332016a11badbdbf01464 MD5 | raw file
Possible License(s): LGPL-2.1, CC-BY-SA-3.0
  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;
  16. using System.Diagnostics;
  17. using System.Dynamic;
  18. using System.Linq.Expressions;
  19. using System.Runtime.CompilerServices;
  20. using System.Threading;
  21. using Microsoft.Scripting;
  22. using Microsoft.Scripting.Generation;
  23. using Microsoft.Scripting.Runtime;
  24. using IronPython.Runtime.Types;
  25. using IronPython.Runtime.Binding;
  26. namespace IronPython.Runtime.Operations {
  27. // These operations get linked into all new-style classes.
  28. public static class UserTypeOps {
  29. public static string ToStringReturnHelper(object o) {
  30. if (o is string && o != null) {
  31. return (string)o;
  32. }
  33. throw PythonOps.TypeError("__str__ returned non-string type ({0})", PythonTypeOps.GetName(o));
  34. }
  35. public static IAttributesCollection SetDictHelper(ref IAttributesCollection iac, IAttributesCollection value) {
  36. if (System.Threading.Interlocked.CompareExchange<IAttributesCollection>(ref iac, value, null) == null)
  37. return value;
  38. return iac;
  39. }
  40. public static object GetPropertyHelper(object prop, object instance, SymbolId name) {
  41. PythonTypeSlot desc = prop as PythonTypeSlot;
  42. if (desc == null) {
  43. throw PythonOps.TypeError("Expected property for {0}, but found {1}",
  44. name.ToString(), DynamicHelpers.GetPythonType(prop).Name);
  45. }
  46. object value;
  47. desc.TryGetValue(DefaultContext.Default, instance, DynamicHelpers.GetPythonType(instance), out value);
  48. return value;
  49. }
  50. public static void SetPropertyHelper(object prop, object instance, object newValue, SymbolId name) {
  51. PythonTypeSlot desc = prop as PythonTypeSlot;
  52. if (desc == null) {
  53. throw PythonOps.TypeError("Expected settable property for {0}, but found {1}",
  54. name.ToString(), DynamicHelpers.GetPythonType(prop).Name);
  55. }
  56. desc.TrySetValue(DefaultContext.Default, instance, DynamicHelpers.GetPythonType(instance), newValue);
  57. }
  58. public static bool SetWeakRefHelper(IPythonObject obj, WeakRefTracker value) {
  59. if (!obj.PythonType.IsWeakReferencable) {
  60. return false;
  61. }
  62. object[] slots = obj.GetSlotsCreate();
  63. slots[slots.Length - 1] = value;
  64. return true;
  65. }
  66. public static WeakRefTracker GetWeakRefHelper(IPythonObject obj) {
  67. object[] slots = obj.GetSlots();
  68. if (slots == null) {
  69. return null;
  70. }
  71. return (WeakRefTracker)slots[slots.Length - 1];
  72. }
  73. public static void SetFinalizerHelper(IPythonObject obj, WeakRefTracker value) {
  74. object[] slots = obj.GetSlotsCreate();
  75. if (Interlocked.CompareExchange(ref slots[slots.Length - 1], value, null) != null) {
  76. GC.SuppressFinalize(value);
  77. }
  78. }
  79. public static object[] GetSlotsCreate(IPythonObject obj, ref object[] slots) {
  80. if (slots != null) {
  81. return slots;
  82. }
  83. Interlocked.CompareExchange(
  84. ref slots,
  85. new object[obj.PythonType.SlotCount + 1], // weakref is stored at the end
  86. null);
  87. return slots;
  88. }
  89. public static void AddRemoveEventHelper(object method, IPythonObject instance, object eventValue, SymbolId name) {
  90. object callable = method;
  91. // TODO: dt gives us a PythonContext which we should use
  92. PythonType dt = instance.PythonType;
  93. PythonTypeSlot dts = method as PythonTypeSlot;
  94. if (dts != null) {
  95. if (!dts.TryGetValue(DefaultContext.Default, instance, dt, out callable))
  96. throw PythonOps.AttributeErrorForMissingAttribute(dt.Name, name);
  97. }
  98. if (!PythonOps.IsCallable(DefaultContext.Default, callable)) {
  99. throw PythonOps.TypeError("Expected callable value for {0}, but found {1}", name.ToString(),
  100. PythonTypeOps.GetName(method));
  101. }
  102. PythonCalls.Call(callable, eventValue);
  103. }
  104. public static DynamicMetaObject/*!*/ GetMetaObjectHelper(IPythonObject self, Expression/*!*/ parameter, DynamicMetaObject baseMetaObject) {
  105. return new Binding.MetaUserObject(parameter, BindingRestrictions.Empty, baseMetaObject, self);
  106. }
  107. public static bool TryGetMixedNewStyleOldStyleSlot(CodeContext context, object instance, SymbolId name, out object value) {
  108. IPythonObject sdo = instance as IPythonObject;
  109. if (sdo != null) {
  110. IAttributesCollection iac = sdo.Dict;
  111. if (iac != null && iac.TryGetValue(name, out value)) {
  112. return true;
  113. }
  114. }
  115. PythonType dt = DynamicHelpers.GetPythonType(instance);
  116. foreach (PythonType type in dt.ResolutionOrder) {
  117. PythonTypeSlot dts;
  118. if (type != TypeCache.Object && type.OldClass != null) {
  119. // we're an old class, check the old-class way
  120. OldClass oc = type.OldClass;
  121. if (oc.TryGetBoundCustomMember(context, name, out value)) {
  122. value = oc.GetOldStyleDescriptor(context, value, instance, oc);
  123. return true;
  124. }
  125. } else if (type.TryLookupSlot(context, name, out dts)) {
  126. // we're a dynamic type, check the dynamic type way
  127. return dts.TryGetValue(context, instance, dt, out value);
  128. }
  129. }
  130. value = null;
  131. return false;
  132. }
  133. public static object SetDictionaryValue(IPythonObject self, SymbolId name, object value) {
  134. IAttributesCollection dict = GetDictionary(self);
  135. return dict[name] = value;
  136. }
  137. public static object FastSetDictionaryValue(ref IAttributesCollection dict, SymbolId name, object value) {
  138. if (dict == null) {
  139. Interlocked.CompareExchange(ref dict, PythonDictionary.MakeSymbolDictionary(), null);
  140. }
  141. return dict[name] = value;
  142. }
  143. public static object RemoveDictionaryValue(IPythonObject self, SymbolId name) {
  144. IAttributesCollection dict = self.Dict;
  145. if (dict != null) {
  146. if (dict.Remove(name)) {
  147. return null;
  148. }
  149. }
  150. throw PythonOps.AttributeErrorForMissingAttribute(self.PythonType, name);
  151. }
  152. internal static IAttributesCollection GetDictionary(IPythonObject self) {
  153. IAttributesCollection dict = self.Dict;
  154. if (dict == null && self.PythonType.HasDictionary) {
  155. dict = self.SetDict(PythonDictionary.MakeSymbolDictionary());
  156. }
  157. return dict;
  158. }
  159. /// <summary>
  160. /// Object.ToString() displays the CLI type name. But we want to display the class name (e.g.
  161. /// '&lt;foo object at 0x000000000000002C&gt;' unless we've overridden __repr__ but not __str__ in
  162. /// which case we'll display the result of __repr__.
  163. /// </summary>
  164. public static string ToStringHelper(IPythonObject o) {
  165. return ObjectOps.__str__(DefaultContext.Default, o);
  166. }
  167. public static bool TryGetNonInheritedMethodHelper(PythonType dt, object instance, SymbolId name, out object callTarget) {
  168. // search MRO for other user-types in the chain that are overriding the method
  169. foreach (PythonType type in dt.ResolutionOrder) {
  170. if (type.IsSystemType) break; // hit the .NET types, we're done
  171. if (LookupValue(type, instance, name, out callTarget)) {
  172. return true;
  173. }
  174. }
  175. // check instance
  176. IPythonObject isdo = instance as IPythonObject;
  177. IAttributesCollection iac;
  178. if (isdo != null && (iac = isdo.Dict) != null) {
  179. if (iac.TryGetValue(name, out callTarget))
  180. return true;
  181. }
  182. callTarget = null;
  183. return false;
  184. }
  185. private static bool LookupValue(PythonType dt, object instance, SymbolId name, out object value) {
  186. PythonTypeSlot dts;
  187. if (dt.TryLookupSlot(DefaultContext.Default, name, out dts) &&
  188. dts.TryGetValue(DefaultContext.Default, instance, dt, out value)) {
  189. return true;
  190. }
  191. value = null;
  192. return false;
  193. }
  194. public static bool TryGetNonInheritedValueHelper(IPythonObject instance, SymbolId name, out object callTarget) {
  195. PythonType dt = instance.PythonType;
  196. PythonTypeSlot dts;
  197. // search MRO for other user-types in the chain that are overriding the method
  198. foreach (PythonType type in dt.ResolutionOrder) {
  199. if (type.IsSystemType) break; // hit the .NET types, we're done
  200. if (type.TryLookupSlot(DefaultContext.Default, name, out dts)) {
  201. callTarget = dts;
  202. return true;
  203. }
  204. }
  205. // check instance
  206. IPythonObject isdo = instance as IPythonObject;
  207. IAttributesCollection iac;
  208. if (isdo != null && (iac = isdo.Dict) != null) {
  209. if (iac.TryGetValue(name, out callTarget))
  210. return true;
  211. }
  212. callTarget = null;
  213. return false;
  214. }
  215. public static object GetAttribute(CodeContext/*!*/ context, object self, string name, PythonTypeSlot getAttributeSlot, PythonTypeSlot getAttrSlot, SiteLocalStorage<CallSite<Func<CallSite, CodeContext, object, string, object>>>/*!*/ callSite) {
  216. object value;
  217. if (callSite.Data == null) {
  218. callSite.Data = MakeGetAttrSite(context);
  219. }
  220. try {
  221. if (getAttributeSlot.TryGetValue(context, self, ((IPythonObject)self).PythonType, out value)) {
  222. return callSite.Data.Target(callSite.Data, context, value, name);
  223. }
  224. } catch (MissingMemberException) {
  225. if (getAttrSlot != null && getAttrSlot.TryGetValue(context, self, ((IPythonObject)self).PythonType, out value)) {
  226. return callSite.Data.Target(callSite.Data, context, value, name);
  227. }
  228. throw;
  229. }
  230. if (getAttrSlot != null && getAttrSlot.TryGetValue(context, self, ((IPythonObject)self).PythonType, out value)) {
  231. return callSite.Data.Target(callSite.Data, context, value, name);
  232. }
  233. throw PythonOps.AttributeError(name);
  234. }
  235. public static object GetAttributeNoThrow(CodeContext/*!*/ context, object self, string name, PythonTypeSlot getAttributeSlot, PythonTypeSlot getAttrSlot, SiteLocalStorage<CallSite<Func<CallSite, CodeContext, object, string, object>>>/*!*/ callSite) {
  236. object value;
  237. if (callSite.Data == null) {
  238. callSite.Data = MakeGetAttrSite(context);
  239. }
  240. try {
  241. if (getAttributeSlot.TryGetValue(context, self, ((IPythonObject)self).PythonType, out value)) {
  242. return callSite.Data.Target(callSite.Data, context, value, name);
  243. }
  244. } catch (MissingMemberException) {
  245. try {
  246. if (getAttrSlot != null && getAttrSlot.TryGetValue(context, self, ((IPythonObject)self).PythonType, out value)) {
  247. return callSite.Data.Target(callSite.Data, context, value, name);
  248. }
  249. return OperationFailed.Value;
  250. } catch (MissingMemberException) {
  251. return OperationFailed.Value;
  252. }
  253. }
  254. try {
  255. if (getAttrSlot != null && getAttrSlot.TryGetValue(context, self, ((IPythonObject)self).PythonType, out value)) {
  256. return callSite.Data.Target(callSite.Data, context, value, name);
  257. }
  258. } catch (MissingMemberException) {
  259. }
  260. return OperationFailed.Value;
  261. }
  262. private static CallSite<Func<CallSite, CodeContext, object, string, object>> MakeGetAttrSite(CodeContext context) {
  263. return CallSite<Func<CallSite, CodeContext, object, string, object>>.Create(
  264. PythonContext.GetContext(context).InvokeOne
  265. );
  266. }
  267. #region IValueEquality Helpers
  268. public static int GetValueHashCodeHelper(object self) {
  269. // new-style classes only lookup in slots, not in instance
  270. // members
  271. object func;
  272. if (DynamicHelpers.GetPythonType(self).TryGetBoundMember(DefaultContext.Default, self, Symbols.Hash, out func)) {
  273. return Converter.ConvertToInt32(PythonCalls.Call(func));
  274. }
  275. return self.GetHashCode();
  276. }
  277. public static bool ValueEqualsHelper(object self, object other) {
  278. object res = RichEqualsHelper(self, other);
  279. if (res != NotImplementedType.Value && res != null && res.GetType() == typeof(bool))
  280. return (bool)res;
  281. return false;
  282. }
  283. private static object RichEqualsHelper(object self, object other) {
  284. object res;
  285. if (PythonTypeOps.TryInvokeBinaryOperator(DefaultContext.Default, self, other, Symbols.OperatorEquals, out res))
  286. return res;
  287. return NotImplementedType.Value;
  288. }
  289. #endregion
  290. internal static Binding.FastBindResult<T> MakeGetBinding<T>(CodeContext codeContext, CallSite<T> site, IPythonObject self, Binding.PythonGetMemberBinder getBinder) where T : class {
  291. Type finalType = PythonTypeOps.GetFinalSystemType(self.PythonType.UnderlyingSystemType);
  292. if (typeof(IDynamicMetaObjectProvider).IsAssignableFrom(finalType) &&
  293. !(self is IFastGettable)) {
  294. // very tricky, user is inheriting from a class which implements IDO, we
  295. // don't optimize this yet.
  296. return new Binding.FastBindResult<T>();
  297. }
  298. return (Binding.FastBindResult<T>)(object)new Binding.MetaUserObject.FastGetBinderHelper(
  299. codeContext,
  300. (CallSite<Func<CallSite, object, CodeContext, object>>)(object)site,
  301. self,
  302. getBinder).GetBinding(codeContext, getBinder.Name);
  303. }
  304. internal static FastBindResult<T> MakeSetBinding<T>(CodeContext codeContext, CallSite<T> site, IPythonObject self, object value, Binding.PythonSetMemberBinder setBinder) where T : class {
  305. if (typeof(IDynamicMetaObjectProvider).IsAssignableFrom(self.GetType().BaseType)) {
  306. // very tricky, user is inheriting from a class which implements IDO, we
  307. // don't optimize this yet.
  308. return new FastBindResult<T>();
  309. }
  310. // optimized versions for possible literals that can show up in code.
  311. Type setType = typeof(T);
  312. if (setType == typeof(Func<CallSite, object, object, object>)) {
  313. return (FastBindResult<T>)(object)new Binding.MetaUserObject.FastSetBinderHelper<object>(
  314. codeContext,
  315. (CallSite<Func<CallSite, object, object, object>>)(object)site,
  316. self,
  317. value,
  318. setBinder).MakeSet();
  319. } else if (setType == typeof(Func<CallSite, object, string, object>)) {
  320. return (FastBindResult<T>)(object)new Binding.MetaUserObject.FastSetBinderHelper<string>(
  321. codeContext,
  322. (CallSite<Func<CallSite, object, string, object>>)(object)site,
  323. self,
  324. value,
  325. setBinder).MakeSet();
  326. } else if (setType == typeof(Func<CallSite, object, int, object>)) {
  327. return (FastBindResult<T>)(object)new Binding.MetaUserObject.FastSetBinderHelper<int>(
  328. codeContext,
  329. (CallSite<Func<CallSite, object, int, object>>)(object)site,
  330. self,
  331. value,
  332. setBinder).MakeSet();
  333. } else if (setType == typeof(Func<CallSite, object, double, object>)) {
  334. return (FastBindResult<T>)(object)new Binding.MetaUserObject.FastSetBinderHelper<double>(
  335. codeContext,
  336. (CallSite<Func<CallSite, object, double, object>>)(object)site,
  337. self,
  338. value,
  339. setBinder).MakeSet();
  340. } else if (setType == typeof(Func<CallSite, object, List, object>)) {
  341. return (FastBindResult<T>)(object)new Binding.MetaUserObject.FastSetBinderHelper<List>(
  342. codeContext,
  343. (CallSite<Func<CallSite, object, List, object>>)(object)site,
  344. self,
  345. value,
  346. setBinder).MakeSet();
  347. } else if (setType == typeof(Func<CallSite, object, PythonTuple, object>)) {
  348. return (FastBindResult<T>)(object)new Binding.MetaUserObject.FastSetBinderHelper<PythonTuple>(
  349. codeContext,
  350. (CallSite<Func<CallSite, object, PythonTuple, object>>)(object)site,
  351. self,
  352. value,
  353. setBinder).MakeSet();
  354. } else if (setType == typeof(Func<CallSite, object, PythonDictionary, object>)) {
  355. return (FastBindResult<T>)(object)new Binding.MetaUserObject.FastSetBinderHelper<PythonDictionary>(
  356. codeContext,
  357. (CallSite<Func<CallSite, object, PythonDictionary, object>>)(object)site,
  358. self,
  359. value,
  360. setBinder).MakeSet();
  361. }
  362. return new FastBindResult<T>();
  363. }
  364. }
  365. }