PageRenderTime 57ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

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

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