PageRenderTime 44ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/DICK.B1/IronPython/Runtime/Binding/MetaPythonType.Members.cs

https://bitbucket.org/williamybs/uidipythontool
C# | 892 lines | 702 code | 142 blank | 48 comment | 76 complexity | 7fb9224691882bd1375beeb5f6a885dc MD5 | raw file
  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. #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.Reflection;
  25. using System.Runtime.CompilerServices;
  26. using System.Threading;
  27. using Microsoft.Scripting;
  28. using Microsoft.Scripting.Actions;
  29. using Microsoft.Scripting.Generation;
  30. using Microsoft.Scripting.Runtime;
  31. using Microsoft.Scripting.Utils;
  32. using IronPython.Runtime.Operations;
  33. using IronPython.Runtime.Types;
  34. namespace IronPython.Runtime.Binding {
  35. using Ast = Expression;
  36. using AstUtils = Microsoft.Scripting.Ast.Utils;
  37. partial class MetaPythonType : MetaPythonObject, IPythonGetable {
  38. #region MetaObject Overrides
  39. public override DynamicMetaObject/*!*/ BindGetMember(GetMemberBinder/*!*/ member) {
  40. return GetMemberWorker(member, PythonContext.GetCodeContext(member));
  41. }
  42. private ValidationInfo GetTypeTest() {
  43. int version = Value.Version;
  44. return new ValidationInfo(
  45. Ast.Call(
  46. typeof(PythonOps).GetMethod("CheckSpecificTypeVersion"),
  47. AstUtils.Convert(Expression, typeof(PythonType)),
  48. AstUtils.Constant(version)
  49. )
  50. );
  51. }
  52. public override DynamicMetaObject/*!*/ BindSetMember(SetMemberBinder/*!*/ member, DynamicMetaObject/*!*/ value) {
  53. PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Type SetMember " + Value.UnderlyingSystemType.FullName);
  54. PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, "Type SetMember");
  55. PythonContext state = PythonContext.GetPythonContext(member);
  56. if (Value.IsSystemType) {
  57. MemberTracker tt = MemberTracker.FromMemberInfo(Value.UnderlyingSystemType);
  58. MemberGroup mg = state.Binder.GetMember(MemberRequestKind.Set, Value.UnderlyingSystemType, member.Name);
  59. // filter protected member access against .NET types, these can only be accessed from derived types...
  60. foreach (MemberTracker mt in mg) {
  61. if (IsProtectedSetter(mt)) {
  62. return new DynamicMetaObject(
  63. BindingHelpers.TypeErrorForProtectedMember(Value.UnderlyingSystemType, member.Name),
  64. Restrictions.Merge(value.Restrictions).Merge(BindingRestrictions.GetInstanceRestriction(Expression, Value))
  65. );
  66. }
  67. }
  68. // have the default binder perform it's operation against a TypeTracker and then
  69. // replace the test w/ our own.
  70. return new DynamicMetaObject(
  71. state.Binder.SetMember(
  72. member.Name,
  73. new DynamicMetaObject(
  74. AstUtils.Constant(tt),
  75. BindingRestrictions.Empty,
  76. tt
  77. ),
  78. value,
  79. new PythonOverloadResolverFactory(state.Binder, AstUtils.Constant(state.SharedContext))
  80. ).Expression,
  81. Restrictions.Merge(value.Restrictions).Merge(BindingRestrictions.GetInstanceRestriction(Expression, Value))
  82. );
  83. }
  84. return MakeSetMember(member, value);
  85. }
  86. public override DynamicMetaObject/*!*/ BindDeleteMember(DeleteMemberBinder/*!*/ member) {
  87. PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Type DeleteMember " + Value.UnderlyingSystemType.FullName);
  88. PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, "Type DeleteMember");
  89. if (Value.IsSystemType) {
  90. PythonContext state = PythonContext.GetPythonContext(member);
  91. MemberTracker tt = MemberTracker.FromMemberInfo(Value.UnderlyingSystemType);
  92. // have the default binder perform it's operation against a TypeTracker and then
  93. // replace the test w/ our own.
  94. return new DynamicMetaObject(
  95. state.Binder.DeleteMember(
  96. member.Name,
  97. new DynamicMetaObject(
  98. AstUtils.Constant(tt),
  99. BindingRestrictions.Empty,
  100. tt
  101. ),
  102. state.SharedOverloadResolverFactory
  103. ).Expression,
  104. BindingRestrictions.GetInstanceRestriction(Expression, Value).Merge(Restrictions)
  105. );
  106. }
  107. return MakeDeleteMember(member);
  108. }
  109. #endregion
  110. #region IPythonGetable Members
  111. public DynamicMetaObject/*!*/ GetMember(PythonGetMemberBinder/*!*/ member, DynamicMetaObject/*!*/ codeContext) {
  112. return GetMemberWorker(member, codeContext.Expression);
  113. }
  114. #endregion
  115. #region Gets
  116. private DynamicMetaObject/*!*/ GetMemberWorker(DynamicMetaObjectBinder/*!*/ member, Expression codeContext) {
  117. PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Type GetMember " + Value.UnderlyingSystemType.FullName);
  118. PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, "Type GetMember");
  119. return new MetaGetBinderHelper(this, member, codeContext, GetTypeTest(), MakeMetaTypeTest(Restrict(this.GetRuntimeType()).Expression)).MakeTypeGetMember();
  120. }
  121. private ValidationInfo MakeMetaTypeTest(Expression self) {
  122. PythonType metaType = DynamicHelpers.GetPythonType(Value);
  123. if (!metaType.IsSystemType) {
  124. int version = metaType.Version;
  125. return new ValidationInfo(
  126. Ast.Call(
  127. typeof(PythonOps).GetMethod("CheckTypeVersion"),
  128. self,
  129. AstUtils.Constant(version)
  130. )
  131. );
  132. }
  133. return ValidationInfo.Empty;
  134. }
  135. /// <summary>
  136. /// Base class for performing member binding. Derived classes override Add methods
  137. /// to produce the actual final result based upon what the GetBinderHelper resolves.
  138. /// </summary>
  139. /// <typeparam name="TResult"></typeparam>
  140. public abstract class GetBinderHelper<TResult> {
  141. private readonly PythonType _value;
  142. private readonly string _name;
  143. internal readonly CodeContext/*!*/ _context;
  144. public GetBinderHelper(PythonType value, CodeContext/*!*/ context, string name) {
  145. _value = value;
  146. _name = name;
  147. _context = context;
  148. }
  149. #region Abstract members
  150. protected abstract TResult Finish(bool metaOnly);
  151. protected abstract void AddError();
  152. protected abstract void AddMetaGetAttribute(PythonType metaType, PythonTypeSlot pts);
  153. protected abstract bool AddMetaSlotAccess(PythonType pt, PythonTypeSlot pts);
  154. protected abstract void AddMetaOldClassAccess();
  155. protected abstract bool AddSlotAccess(PythonType pt, PythonTypeSlot pts);
  156. protected abstract void AddOldClassAccess(PythonType pt);
  157. #endregion
  158. #region Common Get Code
  159. public TResult MakeTypeGetMember() {
  160. PythonTypeSlot pts;
  161. bool isFinal = false, metaOnly = false;
  162. CodeContext lookupContext = PythonContext.GetContext(_context).SharedClsContext;
  163. // first look in the meta-class to see if we have a get/set descriptor
  164. PythonType metaType = DynamicHelpers.GetPythonType(Value);
  165. foreach (PythonType pt in metaType.ResolutionOrder) {
  166. if (pt.TryLookupSlot(lookupContext, _name, out pts) && pts.IsSetDescriptor(lookupContext, metaType)) {
  167. if (AddMetaSlotAccess(metaType, pts)) {
  168. metaOnly = isFinal = true;
  169. break;
  170. }
  171. }
  172. }
  173. if (!isFinal) {
  174. // then search the MRO to see if we have the value
  175. foreach (PythonType pt in Value.ResolutionOrder) {
  176. if (pt.IsOldClass) {
  177. // mixed new-style/old-style class, search the one slot in it's MRO for the member
  178. AddOldClassAccess(pt);
  179. } else if (pt.TryLookupSlot(lookupContext, _name, out pts)) {
  180. if (AddSlotAccess(pt, pts)) {
  181. isFinal = true;
  182. break;
  183. }
  184. }
  185. }
  186. }
  187. if (!isFinal) {
  188. // then go back to the meta class to see if we have a normal attribute
  189. foreach (PythonType pt in metaType.ResolutionOrder) {
  190. if (pt.OldClass != null) {
  191. // mixed new-style/old-style class, just call our version of __getattribute__
  192. // and let it sort it out at runtime.
  193. AddMetaOldClassAccess();
  194. isFinal = true;
  195. break;
  196. } else if (pt.TryLookupSlot(lookupContext, _name, out pts)) {
  197. if (AddMetaSlotAccess(metaType, pts)) {
  198. isFinal = true;
  199. break;
  200. }
  201. }
  202. }
  203. }
  204. if (!isFinal) {
  205. // the member doesn't exist anywhere in the type hierarchy, see if
  206. // we define __getattr__ on our meta type.
  207. if (metaType.TryResolveSlot(_context, "__getattr__", out pts) &&
  208. !pts.IsSetDescriptor(lookupContext, metaType)) { // we tried get/set descriptors initially
  209. AddMetaGetAttribute(metaType, pts);
  210. isFinal = pts.GetAlwaysSucceeds;
  211. }
  212. }
  213. if (!isFinal) {
  214. AddError();
  215. }
  216. return Finish(metaOnly);
  217. }
  218. #endregion
  219. protected PythonType Value {
  220. get {
  221. return _value;
  222. }
  223. }
  224. }
  225. /// <summary>
  226. /// Provides the normal meta binder binding.
  227. /// </summary>
  228. class MetaGetBinderHelper : GetBinderHelper<DynamicMetaObject> {
  229. private readonly DynamicMetaObjectBinder _member;
  230. private readonly MetaPythonType _type;
  231. private readonly Expression _codeContext;
  232. private readonly DynamicMetaObject _restrictedSelf;
  233. private readonly ConditionalBuilder _cb;
  234. private readonly string _symName;
  235. private readonly PythonContext _state;
  236. private readonly ValidationInfo _valInfo, _metaValInfo;
  237. private ParameterExpression _tmp;
  238. public MetaGetBinderHelper(MetaPythonType type, DynamicMetaObjectBinder member, Expression codeContext, ValidationInfo validationInfo, ValidationInfo metaValidation)
  239. : base(type.Value, PythonContext.GetPythonContext(member).SharedContext, GetGetMemberName(member)) {
  240. _member = member;
  241. _codeContext = codeContext;
  242. _type = type;
  243. _cb = new ConditionalBuilder(member);
  244. _symName = GetGetMemberName(member);
  245. _restrictedSelf = new DynamicMetaObject(
  246. AstUtils.Convert(Expression, Value.GetType()),
  247. Restrictions.Merge(BindingRestrictions.GetInstanceRestriction(Expression, Value)),
  248. Value
  249. );
  250. _state = PythonContext.GetPythonContext(member);
  251. _valInfo = validationInfo;
  252. _metaValInfo = metaValidation;
  253. }
  254. protected override void AddOldClassAccess(PythonType pt) {
  255. EnsureTmp();
  256. _cb.AddCondition(
  257. Ast.Call(
  258. typeof(PythonOps).GetMethod("OldClassTryLookupOneSlot"),
  259. AstUtils.Constant(pt),
  260. AstUtils.Constant(pt.OldClass),
  261. AstUtils.Constant(_symName),
  262. _tmp
  263. ),
  264. _tmp
  265. );
  266. }
  267. private void EnsureTmp() {
  268. if (_tmp == null) {
  269. _tmp = Ast.Variable(typeof(object), "tmp");
  270. _cb.AddVariable(_tmp);
  271. }
  272. }
  273. protected override bool AddSlotAccess(PythonType pt, PythonTypeSlot pts) {
  274. pts.MakeGetExpression(
  275. _state.Binder,
  276. _codeContext,
  277. null,
  278. new DynamicMetaObject(
  279. AstUtils.Convert(AstUtils.WeakConstant(Value), typeof(PythonType)),
  280. BindingRestrictions.Empty,
  281. Value
  282. ),
  283. _cb
  284. );
  285. if (!pts.IsAlwaysVisible) {
  286. _cb.AddCondition(Ast.Call(typeof(PythonOps).GetMethod("IsClsVisible"), _codeContext));
  287. return false;
  288. }
  289. return pts.GetAlwaysSucceeds;
  290. }
  291. protected override void AddMetaOldClassAccess() {
  292. // mixed new-style/old-style class, just call our version of __getattribute__
  293. // and let it sort it out at runtime.
  294. _cb.FinishCondition(
  295. Ast.Call(
  296. AstUtils.Convert(
  297. Expression,
  298. typeof(PythonType)
  299. ),
  300. typeof(PythonType).GetMethod("__getattribute__"),
  301. _codeContext,
  302. AstUtils.Constant(GetGetMemberName(_member))
  303. )
  304. );
  305. }
  306. protected override void AddError() {
  307. // TODO: We should preserve restrictions from the error
  308. _cb.FinishCondition(GetFallbackError(_member).Expression);
  309. }
  310. protected override void AddMetaGetAttribute(PythonType metaType, PythonTypeSlot pts) {
  311. EnsureTmp();
  312. _cb.AddCondition(
  313. Ast.Call(
  314. typeof(PythonOps).GetMethod("SlotTryGetBoundValue"),
  315. _codeContext,
  316. AstUtils.Constant(pts, typeof(PythonTypeSlot)),
  317. Expression,
  318. AstUtils.Constant(metaType),
  319. _tmp
  320. ),
  321. Ast.Dynamic(
  322. _state.InvokeOne,
  323. typeof(object),
  324. _codeContext,
  325. _tmp,
  326. AstUtils.Constant(GetGetMemberName(_member))
  327. )
  328. );
  329. }
  330. protected override bool AddMetaSlotAccess(PythonType metaType, PythonTypeSlot pts) {
  331. ParameterExpression tmp = Ast.Variable(typeof(object), "slotRes");
  332. pts.MakeGetExpression(
  333. _state.Binder,
  334. _codeContext,
  335. _type,
  336. new DynamicMetaObject(
  337. AstUtils.Constant(metaType),
  338. BindingRestrictions.Empty,
  339. metaType
  340. ),
  341. _cb
  342. );
  343. if (!pts.IsAlwaysVisible) {
  344. _cb.AddCondition(Ast.Call(typeof(PythonOps).GetMethod("IsClsVisible"), _codeContext));
  345. return false;
  346. }
  347. return pts.GetAlwaysSucceeds;
  348. }
  349. protected override DynamicMetaObject/*!*/ Finish(bool metaOnly) {
  350. DynamicMetaObject res = _cb.GetMetaObject(_restrictedSelf);
  351. if (metaOnly) {
  352. res = BindingHelpers.AddDynamicTestAndDefer(
  353. _member,
  354. res,
  355. new DynamicMetaObject[] { _type },
  356. _metaValInfo
  357. );
  358. } else if (!Value.IsSystemType) {
  359. res = BindingHelpers.AddDynamicTestAndDefer(
  360. _member,
  361. res,
  362. new DynamicMetaObject[] { _type },
  363. _valInfo
  364. );
  365. }
  366. return res;
  367. }
  368. private DynamicMetaObject/*!*/ GetFallbackError(DynamicMetaObjectBinder/*!*/ member) {
  369. if (member is PythonGetMemberBinder) {
  370. // accessing from Python, produce our error
  371. PythonGetMemberBinder pb = member as PythonGetMemberBinder;
  372. if (pb.IsNoThrow) {
  373. return new DynamicMetaObject(
  374. Expression.Constant(OperationFailed.Value),
  375. BindingRestrictions.GetInstanceRestriction(Expression, Value).Merge(Restrictions)
  376. );
  377. } else {
  378. return new DynamicMetaObject(
  379. Ast.Throw(
  380. Ast.Call(
  381. typeof(PythonOps).GetMethod(
  382. "AttributeErrorForMissingAttribute",
  383. new Type[] { typeof(string), typeof(string) }
  384. ),
  385. AstUtils.Constant(DynamicHelpers.GetPythonType(Value).Name),
  386. AstUtils.Constant(pb.Name)
  387. ),
  388. typeof(object)
  389. ),
  390. BindingRestrictions.GetInstanceRestriction(Expression, Value).Merge(Restrictions)
  391. );
  392. }
  393. }
  394. // let the calling language bind the .NET members
  395. return ((GetMemberBinder)member).FallbackGetMember(_type);
  396. }
  397. private Expression/*!*/ Expression {
  398. get {
  399. return _type.Expression;
  400. }
  401. }
  402. private BindingRestrictions Restrictions {
  403. get {
  404. return _type.Restrictions;
  405. }
  406. }
  407. }
  408. /// <summary>
  409. /// Provides delegate based fast binding.
  410. /// </summary>
  411. internal class FastGetBinderHelper : GetBinderHelper<TypeGetBase> {
  412. private readonly PythonGetMemberBinder _binder;
  413. private readonly int _version;
  414. private readonly int _metaVersion;
  415. private bool _canOptimize;
  416. private List<FastGetDelegate> _gets = new List<FastGetDelegate>();
  417. public FastGetBinderHelper(PythonType type, CodeContext context, PythonGetMemberBinder binder)
  418. : base(type, context, binder.Name) {
  419. // capture these before we start producing the result
  420. _version = type.Version;
  421. _metaVersion = DynamicHelpers.GetPythonType(type).Version;
  422. _binder = binder;
  423. }
  424. public Func<CallSite, object, CodeContext, object> GetBinding() {
  425. Dictionary<string, TypeGetBase> cachedGets = GetCachedGets();
  426. TypeGetBase dlg;
  427. lock (cachedGets) {
  428. if (!cachedGets.TryGetValue(_binder.Name, out dlg) || !dlg.IsValid(Value)) {
  429. var binding = MakeTypeGetMember();
  430. if (binding != null) {
  431. dlg = cachedGets[_binder.Name] = binding;
  432. }
  433. }
  434. }
  435. if (dlg != null && dlg.ShouldUseNonOptimizedSite) {
  436. return dlg._func;
  437. }
  438. return null;
  439. }
  440. private Dictionary<string, TypeGetBase> GetCachedGets() {
  441. if (_binder.IsNoThrow) {
  442. Dictionary<string, TypeGetBase> cachedGets = Value._cachedTypeTryGets;
  443. if (cachedGets == null) {
  444. Interlocked.CompareExchange(
  445. ref Value._cachedTypeTryGets,
  446. new Dictionary<string, TypeGetBase>(),
  447. null);
  448. cachedGets = Value._cachedTypeTryGets;
  449. }
  450. return cachedGets;
  451. } else {
  452. Dictionary<string, TypeGetBase> cachedGets = Value._cachedTypeGets;
  453. if (cachedGets == null) {
  454. Interlocked.CompareExchange(
  455. ref Value._cachedTypeGets,
  456. new Dictionary<string, TypeGetBase>(),
  457. null);
  458. cachedGets = Value._cachedTypeGets;
  459. }
  460. return cachedGets;
  461. }
  462. }
  463. protected override void AddOldClassAccess(PythonType pt) {
  464. _gets.Add(new OldClassDelegate(Value, pt, _binder.Name).Target);
  465. }
  466. class OldClassDelegate {
  467. private readonly WeakReference _type, _declType;
  468. private readonly string _name;
  469. public OldClassDelegate(PythonType declType, PythonType oldClass, string name) {
  470. _type = oldClass.GetSharedWeakReference();
  471. _declType = declType.GetSharedWeakReference();
  472. _name = name;
  473. }
  474. public bool Target(CodeContext context, object self, out object result) {
  475. return PythonOps.OldClassTryLookupOneSlot((PythonType)_declType.Target, ((PythonType)_type.Target).OldClass, _name, out result);
  476. }
  477. }
  478. protected override bool AddSlotAccess(PythonType pt, PythonTypeSlot pts) {
  479. if (pts.CanOptimizeGets) {
  480. _canOptimize = true;
  481. }
  482. if (pts.IsAlwaysVisible) {
  483. _gets.Add(new SlotAccessDelegate(pts, Value).Target);
  484. return pts.GetAlwaysSucceeds;
  485. } else {
  486. _gets.Add(new SlotAccessDelegate(pts, Value).TargetCheckCls);
  487. return false;
  488. }
  489. }
  490. class SlotAccessDelegate {
  491. private readonly PythonTypeSlot _slot;
  492. private readonly PythonType _owner;
  493. private readonly WeakReference _weakOwner;
  494. private readonly WeakReference _weakSlot;
  495. public SlotAccessDelegate(PythonTypeSlot slot, PythonType owner) {
  496. if (owner.IsSystemType) {
  497. _owner = owner;
  498. _slot = slot;
  499. } else {
  500. _weakOwner = owner.GetSharedWeakReference();
  501. _weakSlot = new WeakReference(slot);
  502. }
  503. }
  504. public bool TargetCheckCls(CodeContext context, object self, out object result) {
  505. if (PythonOps.IsClsVisible(context)) {
  506. return Slot.TryGetValue(context, null, Type, out result);
  507. }
  508. result = null;
  509. return false;
  510. }
  511. public bool Target(CodeContext context, object self, out object result) {
  512. return Slot.TryGetValue(context, null, Type, out result);
  513. }
  514. public bool MetaTargetCheckCls(CodeContext context, object self, out object result) {
  515. if (PythonOps.IsClsVisible(context)) {
  516. return Slot.TryGetValue(context, self, Type, out result);
  517. }
  518. result = null;
  519. return false;
  520. }
  521. public bool MetaTarget(CodeContext context, object self, out object result) {
  522. return Slot.TryGetValue(context, self, Type, out result);
  523. }
  524. private PythonType Type {
  525. get {
  526. return _owner ?? (PythonType)_weakOwner.Target;
  527. }
  528. }
  529. private PythonTypeSlot Slot {
  530. get {
  531. return _slot ?? (PythonTypeSlot)_weakSlot.Target;
  532. }
  533. }
  534. }
  535. protected override void AddMetaOldClassAccess() {
  536. // mixed new-style/old-style class, just call our version of __getattribute__
  537. // and let it sort it out at runtime.
  538. _gets.Add(new MetaOldClassDelegate(_binder.Name).Target);
  539. }
  540. class MetaOldClassDelegate {
  541. private readonly string _name;
  542. public MetaOldClassDelegate(string name) {
  543. _name = name;
  544. }
  545. public bool Target(CodeContext context, object self, out object result) {
  546. result = ((PythonType)self).__getattribute__(context, _name);
  547. return true;
  548. }
  549. }
  550. protected override void AddError() {
  551. if (_binder.IsNoThrow) {
  552. _gets.Add(new ErrorBinder(_binder.Name).TargetNoThrow);
  553. } else {
  554. _gets.Add(new ErrorBinder(_binder.Name).Target);
  555. }
  556. }
  557. protected override void AddMetaGetAttribute(PythonType metaType, PythonTypeSlot pts) {
  558. _gets.Add(new MetaGetAttributeDelegate(_context, pts, metaType, _binder.Name).Target);
  559. }
  560. class MetaGetAttributeDelegate {
  561. private readonly string _name;
  562. private readonly PythonType _metaType;
  563. private readonly WeakReference _weakMetaType;
  564. private readonly PythonTypeSlot _slot;
  565. private readonly WeakReference _weakSlot;
  566. private readonly CallSite<Func<CallSite, CodeContext, object, string, object>> _invokeSite;
  567. public MetaGetAttributeDelegate(CodeContext context, PythonTypeSlot slot, PythonType metaType, string name) {
  568. _name = name;
  569. if (metaType.IsSystemType) {
  570. _metaType = metaType;
  571. _slot = slot;
  572. } else {
  573. _weakMetaType = metaType.GetSharedWeakReference();
  574. _weakSlot = new WeakReference(slot);
  575. }
  576. _invokeSite = CallSite<Func<CallSite, CodeContext, object, string, object>>.Create(PythonContext.GetContext(context).InvokeOne);
  577. }
  578. public bool Target(CodeContext context, object self, out object result) {
  579. object value;
  580. if (Slot.TryGetValue(context, self, MetaType, out value)) {
  581. result = _invokeSite.Target(_invokeSite, context, value, _name);
  582. return true;
  583. }
  584. result = null;
  585. return false;
  586. }
  587. private PythonType MetaType {
  588. get {
  589. return _metaType ?? (PythonType)_weakMetaType.Target;
  590. }
  591. }
  592. private PythonTypeSlot Slot {
  593. get {
  594. return _slot ?? (PythonTypeSlot)_weakSlot.Target;
  595. }
  596. }
  597. }
  598. protected override bool AddMetaSlotAccess(PythonType metaType, PythonTypeSlot pts) {
  599. if (pts.CanOptimizeGets) {
  600. _canOptimize = true;
  601. }
  602. if (pts.IsAlwaysVisible) {
  603. _gets.Add(new SlotAccessDelegate(pts, metaType).MetaTarget);
  604. return pts.GetAlwaysSucceeds;
  605. } else {
  606. _gets.Add(new SlotAccessDelegate(pts, metaType).MetaTargetCheckCls);
  607. return false;
  608. }
  609. }
  610. protected override TypeGetBase/*!*/ Finish(bool metaOnly) {
  611. if (metaOnly) {
  612. if (DynamicHelpers.GetPythonType(Value).IsSystemType) {
  613. return new SystemTypeGet(_binder, _gets.ToArray(), Value, metaOnly, _canOptimize);
  614. } else {
  615. return new TypeGet(_binder, _gets.ToArray(), metaOnly ? _metaVersion : _version, metaOnly, _canOptimize);
  616. }
  617. } else {
  618. if (Value.IsSystemType) {
  619. return new SystemTypeGet(_binder, _gets.ToArray(), Value, metaOnly, _canOptimize);
  620. }
  621. return new TypeGet(_binder, _gets.ToArray(), metaOnly ? _metaVersion : _version, metaOnly, _canOptimize);
  622. }
  623. }
  624. class ErrorBinder {
  625. private readonly string _name;
  626. public ErrorBinder(string name) {
  627. _name = name;
  628. }
  629. public bool TargetNoThrow(CodeContext context, object self, out object result) {
  630. result = OperationFailed.Value;
  631. return true;
  632. }
  633. public bool Target(CodeContext context, object self, out object result) {
  634. throw PythonOps.AttributeErrorForObjectMissingAttribute(self, _name);
  635. }
  636. }
  637. }
  638. #endregion
  639. #region Sets
  640. private DynamicMetaObject/*!*/ MakeSetMember(SetMemberBinder/*!*/ member, DynamicMetaObject/*!*/ value) {
  641. PythonContext state = PythonContext.GetPythonContext(member);
  642. DynamicMetaObject self = Restrict(Value.GetType());
  643. if (Value.GetType() != typeof(PythonType) && DynamicHelpers.GetPythonType(Value).IsSystemType) {
  644. // built-in subclass of .NET type. Usually __setattr__ is handled by MetaUserObject
  645. // but we can have a built-in subtype that's not a user type.
  646. PythonTypeSlot pts;
  647. if (Value.TryGetCustomSetAttr(state.SharedContext, out pts)) {
  648. Debug.Assert(pts.GetAlwaysSucceeds);
  649. ParameterExpression tmp = Ast.Variable(typeof(object), "boundVal");
  650. return BindingHelpers.AddDynamicTestAndDefer(
  651. member,
  652. new DynamicMetaObject(
  653. Ast.Block(
  654. new[] { tmp },
  655. Ast.Dynamic(
  656. state.Invoke(new CallSignature(2)),
  657. typeof(object),
  658. AstUtils.Constant(state.SharedContext),
  659. Ast.Block(
  660. Ast.Call(
  661. typeof(PythonOps).GetMethod("SlotTryGetValue"),
  662. AstUtils.Constant(state.SharedContext),
  663. AstUtils.Convert(AstUtils.WeakConstant(pts), typeof(PythonTypeSlot)),
  664. AstUtils.Convert(Expression, typeof(object)),
  665. AstUtils.Convert(AstUtils.WeakConstant(DynamicHelpers.GetPythonType(Value)), typeof(PythonType)),
  666. tmp
  667. ),
  668. tmp
  669. ),
  670. Ast.Constant(member.Name),
  671. value.Expression
  672. )
  673. ),
  674. self.Restrictions
  675. ),
  676. new DynamicMetaObject[] { this, value },
  677. TestUserType()
  678. );
  679. }
  680. }
  681. return BindingHelpers.AddDynamicTestAndDefer(
  682. member,
  683. new DynamicMetaObject(
  684. Ast.Call(
  685. typeof(PythonOps).GetMethod("PythonTypeSetCustomMember"),
  686. AstUtils.Constant(PythonContext.GetPythonContext(member).SharedContext),
  687. self.Expression,
  688. AstUtils.Constant(member.Name),
  689. AstUtils.Convert(
  690. value.Expression,
  691. typeof(object)
  692. )
  693. ),
  694. self.Restrictions.Merge(value.Restrictions)
  695. ),
  696. new DynamicMetaObject[] { this, value },
  697. TestUserType()
  698. );
  699. }
  700. private static bool IsProtectedSetter(MemberTracker mt) {
  701. PropertyTracker pt = mt as PropertyTracker;
  702. if (pt != null) {
  703. MethodInfo mi = pt.GetSetMethod(true);
  704. if (mi != null && mi.IsProtected()) {
  705. return true;
  706. }
  707. }
  708. FieldTracker ft = mt as FieldTracker;
  709. if (ft != null) {
  710. return ft.Field.IsProtected();
  711. }
  712. return false;
  713. }
  714. #endregion
  715. #region Deletes
  716. private DynamicMetaObject/*!*/ MakeDeleteMember(DeleteMemberBinder/*!*/ member) {
  717. DynamicMetaObject self = Restrict(Value.GetType());
  718. return BindingHelpers.AddDynamicTestAndDefer(
  719. member,
  720. new DynamicMetaObject(
  721. Ast.Call(
  722. typeof(PythonOps).GetMethod("PythonTypeDeleteCustomMember"),
  723. AstUtils.Constant(PythonContext.GetPythonContext(member).SharedContext),
  724. self.Expression,
  725. AstUtils.Constant(member.Name)
  726. ),
  727. self.Restrictions
  728. ),
  729. new DynamicMetaObject[] { this },
  730. TestUserType()
  731. );
  732. }
  733. #endregion
  734. #region Helpers
  735. private ValidationInfo/*!*/ TestUserType() {
  736. return new ValidationInfo(
  737. Ast.Not(
  738. Ast.Call(
  739. typeof(PythonOps).GetMethod("IsPythonType"),
  740. AstUtils.Convert(
  741. Expression,
  742. typeof(PythonType)
  743. )
  744. )
  745. )
  746. );
  747. }
  748. #endregion
  749. }
  750. }