PageRenderTime 42ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://github.com/jschementi/iron
C# | 520 lines | 402 code | 79 blank | 39 comment | 132 complexity | 0957fc802c248b4c14184a3571da4520 MD5 | raw file
Possible License(s): ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  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.Collections.Generic;
  22. using System.Diagnostics;
  23. using System.Dynamic;
  24. using System.Runtime.CompilerServices;
  25. using System.Threading;
  26. using Microsoft.Scripting.Runtime;
  27. using Microsoft.Scripting.Utils;
  28. using IronPython.Runtime.Types;
  29. using IronPython.Runtime.Binding;
  30. namespace IronPython.Runtime.Operations {
  31. // These operations get linked into all new-style classes.
  32. public static class UserTypeOps {
  33. public static string ToStringReturnHelper(object o) {
  34. if (o is string && o != null) {
  35. return (string)o;
  36. }
  37. throw PythonOps.TypeError("__str__ returned non-string type ({0})", PythonTypeOps.GetName(o));
  38. }
  39. public static PythonDictionary SetDictHelper(ref PythonDictionary dict, PythonDictionary value) {
  40. if (System.Threading.Interlocked.CompareExchange<PythonDictionary>(ref dict, value, null) == null)
  41. return value;
  42. return dict;
  43. }
  44. public static object GetPropertyHelper(object prop, object instance, string name) {
  45. PythonTypeSlot desc = prop as PythonTypeSlot;
  46. if (desc == null) {
  47. throw PythonOps.TypeError("Expected property for {0}, but found {1}",
  48. name.ToString(), DynamicHelpers.GetPythonType(prop).Name);
  49. }
  50. object value;
  51. desc.TryGetValue(DefaultContext.Default, instance, DynamicHelpers.GetPythonType(instance), out value);
  52. return value;
  53. }
  54. public static void SetPropertyHelper(object prop, object instance, object newValue, string name) {
  55. PythonTypeSlot desc = prop as PythonTypeSlot;
  56. if (desc == null) {
  57. throw PythonOps.TypeError("Expected settable property for {0}, but found {1}",
  58. name.ToString(), DynamicHelpers.GetPythonType(prop).Name);
  59. }
  60. desc.TrySetValue(DefaultContext.Default, instance, DynamicHelpers.GetPythonType(instance), newValue);
  61. }
  62. public static bool SetWeakRefHelper(IPythonObject obj, WeakRefTracker value) {
  63. if (!obj.PythonType.IsWeakReferencable) {
  64. return false;
  65. }
  66. object[] slots = obj.GetSlotsCreate();
  67. slots[slots.Length - 1] = value;
  68. return true;
  69. }
  70. public static WeakRefTracker GetWeakRefHelper(IPythonObject obj) {
  71. object[] slots = obj.GetSlots();
  72. if (slots == null) {
  73. return null;
  74. }
  75. return (WeakRefTracker)slots[slots.Length - 1];
  76. }
  77. public static void SetFinalizerHelper(IPythonObject obj, WeakRefTracker value) {
  78. object[] slots = obj.GetSlotsCreate();
  79. if (Interlocked.CompareExchange(ref slots[slots.Length - 1], value, null) != null) {
  80. GC.SuppressFinalize(value);
  81. }
  82. }
  83. public static object[] GetSlotsCreate(IPythonObject obj, ref object[] slots) {
  84. if (slots != null) {
  85. return slots;
  86. }
  87. Interlocked.CompareExchange(
  88. ref slots,
  89. new object[obj.PythonType.SlotCount + 1], // weakref is stored at the end
  90. null);
  91. return slots;
  92. }
  93. public static void AddRemoveEventHelper(object method, IPythonObject instance, object eventValue, string name) {
  94. object callable = method;
  95. // TODO: dt gives us a PythonContext which we should use
  96. PythonType dt = instance.PythonType;
  97. PythonTypeSlot dts = method as PythonTypeSlot;
  98. if (dts != null) {
  99. if (!dts.TryGetValue(DefaultContext.Default, instance, dt, out callable))
  100. throw PythonOps.AttributeErrorForMissingAttribute(dt.Name, name);
  101. }
  102. if (!PythonOps.IsCallable(DefaultContext.Default, callable)) {
  103. throw PythonOps.TypeError("Expected callable value for {0}, but found {1}", name.ToString(),
  104. PythonTypeOps.GetName(method));
  105. }
  106. PythonCalls.Call(callable, eventValue);
  107. }
  108. public static DynamicMetaObject/*!*/ GetMetaObjectHelper(IPythonObject self, Expression/*!*/ parameter, DynamicMetaObject baseMetaObject) {
  109. return new Binding.MetaUserObject(parameter, BindingRestrictions.Empty, baseMetaObject, self);
  110. }
  111. public static bool TryGetMixedNewStyleOldStyleSlot(CodeContext context, object instance, string name, out object value) {
  112. IPythonObject sdo = instance as IPythonObject;
  113. if (sdo != null) {
  114. PythonDictionary dict = sdo.Dict;
  115. if (dict != null && dict.TryGetValue(name, out value)) {
  116. return true;
  117. }
  118. }
  119. PythonType dt = DynamicHelpers.GetPythonType(instance);
  120. foreach (PythonType type in dt.ResolutionOrder) {
  121. PythonTypeSlot dts;
  122. if (type != TypeCache.Object && type.OldClass != null) {
  123. // we're an old class, check the old-class way
  124. OldClass oc = type.OldClass;
  125. if (oc.TryGetBoundCustomMember(context, name, out value)) {
  126. value = oc.GetOldStyleDescriptor(context, value, instance, oc);
  127. return true;
  128. }
  129. } else if (type.TryLookupSlot(context, name, out dts)) {
  130. // we're a dynamic type, check the dynamic type way
  131. return dts.TryGetValue(context, instance, dt, out value);
  132. }
  133. }
  134. value = null;
  135. return false;
  136. }
  137. public static bool TryGetDictionaryValue(PythonDictionary dict, string name, int keyVersion, int keyIndex, out object res) {
  138. CustomInstanceDictionaryStorage dictStorage;
  139. if (dict != null) {
  140. if ((dictStorage = dict._storage as CustomInstanceDictionaryStorage) != null && dictStorage.KeyVersion == keyVersion) {
  141. if (dictStorage.TryGetValue(keyIndex, out res)) {
  142. return true;
  143. }
  144. } else if (dict.TryGetValue(name, out res)) {
  145. return true;
  146. }
  147. }
  148. res = null;
  149. return false;
  150. }
  151. public static object SetDictionaryValue(IPythonObject self, string name, object value) {
  152. PythonDictionary dict = GetDictionary(self);
  153. return dict[name] = value;
  154. }
  155. public static object SetDictionaryValueOptimized(IPythonObject ipo, string name, object value, int keysVersion, int index) {
  156. var dict = UserTypeOps.GetDictionary(ipo);
  157. CustomInstanceDictionaryStorage storage;
  158. if ((storage = dict._storage as CustomInstanceDictionaryStorage) != null && storage.KeyVersion == keysVersion) {
  159. storage.SetExtraValue(index, value);
  160. } else {
  161. dict[name] = value;
  162. }
  163. return value;
  164. }
  165. public static object FastSetDictionaryValue(ref PythonDictionary dict, string name, object value) {
  166. if (dict == null) {
  167. Interlocked.CompareExchange(ref dict, PythonDictionary.MakeSymbolDictionary(), null);
  168. }
  169. return dict[name] = value;
  170. }
  171. public static object FastSetDictionaryValueOptimized(PythonType type, ref PythonDictionary dict, string name, object value, int keysVersion, int index) {
  172. if (dict == null) {
  173. Interlocked.CompareExchange(ref dict, type.MakeDictionary(), null);
  174. }
  175. CustomInstanceDictionaryStorage storage;
  176. if ((storage = dict._storage as CustomInstanceDictionaryStorage) != null && storage.KeyVersion == keysVersion) {
  177. storage.SetExtraValue(index, value);
  178. return value;
  179. } else {
  180. return dict[name] = value;
  181. }
  182. }
  183. public static object RemoveDictionaryValue(IPythonObject self, string name) {
  184. PythonDictionary dict = self.Dict;
  185. if (dict != null) {
  186. if (dict.Remove(name)) {
  187. return null;
  188. }
  189. }
  190. throw PythonOps.AttributeErrorForMissingAttribute(self.PythonType, name);
  191. }
  192. internal static PythonDictionary GetDictionary(IPythonObject self) {
  193. PythonDictionary dict = self.Dict;
  194. if (dict == null && self.PythonType.HasDictionary) {
  195. dict = self.SetDict(self.PythonType.MakeDictionary());
  196. }
  197. return dict;
  198. }
  199. /// <summary>
  200. /// Object.ToString() displays the CLI type name. But we want to display the class name (e.g.
  201. /// '&lt;foo object at 0x000000000000002C&gt;' unless we've overridden __repr__ but not __str__ in
  202. /// which case we'll display the result of __repr__.
  203. /// </summary>
  204. public static string ToStringHelper(IPythonObject o) {
  205. return ObjectOps.__str__(DefaultContext.Default, o);
  206. }
  207. public static bool TryGetNonInheritedMethodHelper(PythonType dt, object instance, string name, out object callTarget) {
  208. // search MRO for other user-types in the chain that are overriding the method
  209. foreach (PythonType type in dt.ResolutionOrder) {
  210. if (type.IsSystemType) break; // hit the .NET types, we're done
  211. if (LookupValue(type, instance, name, out callTarget)) {
  212. return true;
  213. }
  214. }
  215. // check instance
  216. IPythonObject isdo = instance as IPythonObject;
  217. PythonDictionary dict;
  218. if (isdo != null && (dict = isdo.Dict) != null) {
  219. if (dict.TryGetValue(name, out callTarget))
  220. return true;
  221. }
  222. callTarget = null;
  223. return false;
  224. }
  225. private static bool LookupValue(PythonType dt, object instance, string name, out object value) {
  226. PythonTypeSlot dts;
  227. if (dt.TryLookupSlot(DefaultContext.Default, name, out dts) &&
  228. dts.TryGetValue(DefaultContext.Default, instance, dt, out value)) {
  229. return true;
  230. }
  231. value = null;
  232. return false;
  233. }
  234. public static bool TryGetNonInheritedValueHelper(IPythonObject instance, string name, out object callTarget) {
  235. PythonType dt = instance.PythonType;
  236. PythonTypeSlot dts;
  237. // search MRO for other user-types in the chain that are overriding the method
  238. foreach (PythonType type in dt.ResolutionOrder) {
  239. if (type.IsSystemType) break; // hit the .NET types, we're done
  240. if (type.TryLookupSlot(DefaultContext.Default, name, out dts)) {
  241. callTarget = dts;
  242. return true;
  243. }
  244. }
  245. // check instance
  246. IPythonObject isdo = instance as IPythonObject;
  247. PythonDictionary dict;
  248. if (isdo != null && (dict = isdo.Dict) != null) {
  249. if (dict.TryGetValue(name, out callTarget))
  250. return true;
  251. }
  252. callTarget = null;
  253. return false;
  254. }
  255. public static object GetAttribute(CodeContext/*!*/ context, object self, string name, PythonTypeSlot getAttributeSlot, PythonTypeSlot getAttrSlot, SiteLocalStorage<CallSite<Func<CallSite, CodeContext, object, string, object>>>/*!*/ callSite) {
  256. object value;
  257. if (callSite.Data == null) {
  258. callSite.Data = MakeGetAttrSite(context);
  259. }
  260. try {
  261. if (getAttributeSlot.TryGetValue(context, self, ((IPythonObject)self).PythonType, out value)) {
  262. return callSite.Data.Target(callSite.Data, context, value, name);
  263. }
  264. } catch (MissingMemberException) {
  265. if (getAttrSlot != null && getAttrSlot.TryGetValue(context, self, ((IPythonObject)self).PythonType, out value)) {
  266. return callSite.Data.Target(callSite.Data, context, value, name);
  267. }
  268. throw;
  269. }
  270. if (getAttrSlot != null && getAttrSlot.TryGetValue(context, self, ((IPythonObject)self).PythonType, out value)) {
  271. return callSite.Data.Target(callSite.Data, context, value, name);
  272. }
  273. throw PythonOps.AttributeError(name);
  274. }
  275. public static object GetAttributeNoThrow(CodeContext/*!*/ context, object self, string name, PythonTypeSlot getAttributeSlot, PythonTypeSlot getAttrSlot, SiteLocalStorage<CallSite<Func<CallSite, CodeContext, object, string, object>>>/*!*/ callSite) {
  276. object value;
  277. if (callSite.Data == null) {
  278. callSite.Data = MakeGetAttrSite(context);
  279. }
  280. try {
  281. if (getAttributeSlot.TryGetValue(context, self, ((IPythonObject)self).PythonType, out value)) {
  282. return callSite.Data.Target(callSite.Data, context, value, name);
  283. }
  284. } catch (MissingMemberException) {
  285. try {
  286. if (getAttrSlot != null && getAttrSlot.TryGetValue(context, self, ((IPythonObject)self).PythonType, out value)) {
  287. return callSite.Data.Target(callSite.Data, context, value, name);
  288. }
  289. return OperationFailed.Value;
  290. } catch (MissingMemberException) {
  291. return OperationFailed.Value;
  292. }
  293. }
  294. try {
  295. if (getAttrSlot != null && getAttrSlot.TryGetValue(context, self, ((IPythonObject)self).PythonType, out value)) {
  296. return callSite.Data.Target(callSite.Data, context, value, name);
  297. }
  298. } catch (MissingMemberException) {
  299. }
  300. return OperationFailed.Value;
  301. }
  302. private static CallSite<Func<CallSite, CodeContext, object, string, object>> MakeGetAttrSite(CodeContext context) {
  303. return CallSite<Func<CallSite, CodeContext, object, string, object>>.Create(
  304. PythonContext.GetContext(context).InvokeOne
  305. );
  306. }
  307. #region IValueEquality Helpers
  308. #if CLR2
  309. public static int GetValueHashCodeHelper(object self) {
  310. // new-style classes only lookup in slots, not in instance
  311. // members
  312. object func;
  313. if (DynamicHelpers.GetPythonType(self).TryGetBoundMember(DefaultContext.Default, self, "__hash__", out func)) {
  314. return Converter.ConvertToInt32(PythonCalls.Call(func));
  315. }
  316. return self.GetHashCode();
  317. }
  318. public static bool ValueEqualsHelper(object self, object other) {
  319. object res = RichEqualsHelper(self, other);
  320. if (res != NotImplementedType.Value && res != null && res.GetType() == typeof(bool))
  321. return (bool)res;
  322. return false;
  323. }
  324. private static object RichEqualsHelper(object self, object other) {
  325. object res;
  326. if (PythonTypeOps.TryInvokeBinaryOperator(DefaultContext.Default, self, other, "__eq__", out res))
  327. return res;
  328. return NotImplementedType.Value;
  329. }
  330. #endif
  331. #endregion
  332. internal static Binding.FastBindResult<T> MakeGetBinding<T>(CodeContext codeContext, CallSite<T> site, IPythonObject self, Binding.PythonGetMemberBinder getBinder) where T : class {
  333. Type finalType = self.PythonType.FinalSystemType;
  334. if (typeof(IDynamicMetaObjectProvider).IsAssignableFrom(finalType) &&
  335. !(self is IFastGettable)) {
  336. // very tricky, user is inheriting from a class which implements IDO, we
  337. // don't optimize this yet.
  338. return new Binding.FastBindResult<T>();
  339. }
  340. return (Binding.FastBindResult<T>)(object)new Binding.MetaUserObject.FastGetBinderHelper(
  341. codeContext,
  342. (CallSite<Func<CallSite, object, CodeContext, object>>)(object)site,
  343. self,
  344. getBinder).GetBinding(codeContext, getBinder.Name);
  345. }
  346. internal static FastBindResult<T> MakeSetBinding<T>(CodeContext codeContext, CallSite<T> site, IPythonObject self, object value, Binding.PythonSetMemberBinder setBinder) where T : class {
  347. if (typeof(IDynamicMetaObjectProvider).IsAssignableFrom(self.GetType().BaseType)) {
  348. // very tricky, user is inheriting from a class which implements IDO, we
  349. // don't optimize this yet.
  350. return new FastBindResult<T>();
  351. }
  352. // optimized versions for possible literals that can show up in code.
  353. Type setType = typeof(T);
  354. if (setType == typeof(Func<CallSite, object, object, object>)) {
  355. return (FastBindResult<T>)(object)new Binding.MetaUserObject.FastSetBinderHelper<object>(
  356. codeContext,
  357. self,
  358. value,
  359. setBinder).MakeSet();
  360. } else if (setType == typeof(Func<CallSite, object, string, object>)) {
  361. return (FastBindResult<T>)(object)new Binding.MetaUserObject.FastSetBinderHelper<string>(
  362. codeContext,
  363. self,
  364. value,
  365. setBinder).MakeSet();
  366. } else if (setType == typeof(Func<CallSite, object, int, object>)) {
  367. return (FastBindResult<T>)(object)new Binding.MetaUserObject.FastSetBinderHelper<int>(
  368. codeContext,
  369. self,
  370. value,
  371. setBinder).MakeSet();
  372. } else if (setType == typeof(Func<CallSite, object, double, object>)) {
  373. return (FastBindResult<T>)(object)new Binding.MetaUserObject.FastSetBinderHelper<double>(
  374. codeContext,
  375. self,
  376. value,
  377. setBinder).MakeSet();
  378. } else if (setType == typeof(Func<CallSite, object, List, object>)) {
  379. return (FastBindResult<T>)(object)new Binding.MetaUserObject.FastSetBinderHelper<List>(
  380. codeContext,
  381. self,
  382. value,
  383. setBinder).MakeSet();
  384. } else if (setType == typeof(Func<CallSite, object, PythonTuple, object>)) {
  385. return (FastBindResult<T>)(object)new Binding.MetaUserObject.FastSetBinderHelper<PythonTuple>(
  386. codeContext,
  387. self,
  388. value,
  389. setBinder).MakeSet();
  390. } else if (setType == typeof(Func<CallSite, object, PythonDictionary, object>)) {
  391. return (FastBindResult<T>)(object)new Binding.MetaUserObject.FastSetBinderHelper<PythonDictionary>(
  392. codeContext,
  393. self,
  394. value,
  395. setBinder).MakeSet();
  396. }
  397. return new FastBindResult<T>();
  398. }
  399. }
  400. /// <summary>
  401. /// Provides a debug view for user defined types. This class is declared as public
  402. /// because it is referred to from generated code. You should not use this class.
  403. /// </summary>
  404. public class UserTypeDebugView {
  405. private readonly IPythonObject _userObject;
  406. public UserTypeDebugView(IPythonObject userObject) {
  407. _userObject = userObject;
  408. }
  409. public PythonType __class__ {
  410. get {
  411. return _userObject.PythonType;
  412. }
  413. }
  414. [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
  415. internal List<ObjectDebugView> Members {
  416. get {
  417. var res = new List<ObjectDebugView>();
  418. if (_userObject.Dict != null) {
  419. foreach (var v in _userObject.Dict) {
  420. res.Add(new ObjectDebugView(v.Key, v.Value));
  421. }
  422. }
  423. // collect any slots on the object
  424. object[] slots = _userObject.GetSlots();
  425. if (slots != null) {
  426. var mro = _userObject.PythonType.ResolutionOrder;
  427. List<string> slotNames = new List<string>();
  428. for(int i = mro.Count - 1; i>= 0; i--) {
  429. slotNames.AddRange(mro[i].GetTypeSlots());
  430. }
  431. for (int i = 0; i < slots.Length - 1; i++) {
  432. if (slots[i] != Uninitialized.Instance) {
  433. res.Add(new ObjectDebugView(slotNames[i], slots[i]));
  434. }
  435. }
  436. }
  437. return res;
  438. }
  439. }
  440. }
  441. }