PageRenderTime 46ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

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

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