PageRenderTime 45ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/DICK.B1/IronPython/Runtime/Binding/PythonInvokeBinder.cs

https://bitbucket.org/williamybs/uidipythontool
C# | 311 lines | 213 code | 54 blank | 44 comment | 27 complexity | c5fbf995087e076fb597af74bcec207d 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.Runtime.CompilerServices;
  25. using Microsoft.Scripting;
  26. using Microsoft.Scripting.Actions;
  27. using Microsoft.Scripting.Generation;
  28. using Microsoft.Scripting.Runtime;
  29. using Microsoft.Scripting.Utils;
  30. using IronPython.Runtime.Operations;
  31. using AstUtils = Microsoft.Scripting.Ast.Utils;
  32. namespace IronPython.Runtime.Binding {
  33. /// <summary>
  34. /// The Action used for Python call sites. This supports both splatting of position and keyword arguments.
  35. ///
  36. /// When a foreign object is encountered the arguments are expanded into normal position/keyword arguments.
  37. /// </summary>
  38. class PythonInvokeBinder : DynamicMetaObjectBinder, IPythonSite, IExpressionSerializable {
  39. private readonly PythonContext/*!*/ _context;
  40. private readonly CallSignature _signature;
  41. public PythonInvokeBinder(PythonContext/*!*/ context, CallSignature signature) {
  42. _context = context;
  43. _signature = signature;
  44. }
  45. #region DynamicMetaObjectBinder overrides
  46. /// <summary>
  47. /// Python's Invoke is a non-standard action. Here we first try to bind through a Python
  48. /// internal interface (IPythonInvokable) which supports CallSigantures. If that fails
  49. /// and we have an IDO then we translate to the DLR protocol through a nested dynamic site -
  50. /// this includes unsplatting any keyword / position arguments. Finally if it's just a plain
  51. /// old .NET type we use the default binder which supports CallSignatures.
  52. /// </summary>
  53. public override DynamicMetaObject/*!*/ Bind(DynamicMetaObject/*!*/ target, DynamicMetaObject/*!*/[]/*!*/ args) {
  54. Debug.Assert(args.Length > 0);
  55. DynamicMetaObject cc = target;
  56. DynamicMetaObject actualTarget = args[0];
  57. args = ArrayUtils.RemoveFirst(args);
  58. Debug.Assert(cc.GetLimitType() == typeof(CodeContext));
  59. return BindWorker(cc, actualTarget, args);
  60. }
  61. private DynamicMetaObject BindWorker(DynamicMetaObject/*!*/ context, DynamicMetaObject/*!*/ target, DynamicMetaObject/*!*/[]/*!*/ args) {
  62. // we don't have CodeContext if an IDO falls back to us when we ask them to produce the Call
  63. IPythonInvokable icc = target as IPythonInvokable;
  64. if (icc != null) {
  65. // call it and provide the context
  66. return icc.Invoke(
  67. this,
  68. context.Expression,
  69. target,
  70. args
  71. );
  72. } else if (target.Value is IDynamicMetaObjectProvider) {
  73. return InvokeForeignObject(target, args);
  74. }
  75. #if !SILVERLIGHT
  76. else if (Microsoft.Scripting.ComInterop.ComBinder.CanComBind(target.Value)) {
  77. return InvokeForeignObject(target, args);
  78. }
  79. #endif
  80. return Fallback(context.Expression, target, args);
  81. }
  82. public override T BindDelegate<T>(CallSite<T> site, object[] args) {
  83. IFastInvokable ifi = args[1] as IFastInvokable;
  84. if (ifi != null) {
  85. FastBindResult<T> res = ifi.MakeInvokeBinding(site, this, (CodeContext)args[0], ArrayUtils.ShiftLeft(args, 2));
  86. if (res.Target != null) {
  87. if (res.ShouldCache) {
  88. base.CacheTarget(res.Target);
  89. }
  90. return res.Target;
  91. }
  92. }
  93. if (args[1] is Types.PythonType) {
  94. PerfTrack.NoteEvent(PerfTrack.Categories.BindingSlow, "InvokeNoFast " + ((Types.PythonType)args[1]).Name);
  95. } else {
  96. PerfTrack.NoteEvent(PerfTrack.Categories.BindingSlow, "InvokeNoFast " + CompilerHelpers.GetType(args[1]));
  97. }
  98. return base.BindDelegate(site, args);
  99. }
  100. /// <summary>
  101. /// Fallback - performs the default binding operation if the object isn't recognized
  102. /// as being invokable.
  103. /// </summary>
  104. internal DynamicMetaObject/*!*/ Fallback(Expression codeContext, DynamicMetaObject target, DynamicMetaObject/*!*/[]/*!*/ args) {
  105. if (target.NeedsDeferral()) {
  106. return Defer(args);
  107. }
  108. return PythonProtocol.Call(this, target, args) ??
  109. Context.Binder.Create(Signature, target, args, codeContext) ??
  110. Context.Binder.Call(Signature, new PythonOverloadResolverFactory(Context.Binder, codeContext), target, args);
  111. }
  112. #endregion
  113. #region Object Overrides
  114. public override int GetHashCode() {
  115. return _signature.GetHashCode() ^ _context.Binder.GetHashCode();
  116. }
  117. public override bool Equals(object obj) {
  118. PythonInvokeBinder ob = obj as PythonInvokeBinder;
  119. if (ob == null) {
  120. return false;
  121. }
  122. return ob._context.Binder == _context.Binder &&
  123. _signature == ob._signature;
  124. }
  125. public override string ToString() {
  126. return "Python Invoke " + Signature.ToString();
  127. }
  128. #endregion
  129. #region Public API Surface
  130. /// <summary>
  131. /// Gets the CallSignature for this invocation which describes how the MetaObject array
  132. /// is to be mapped.
  133. /// </summary>
  134. public CallSignature Signature {
  135. get {
  136. return _signature;
  137. }
  138. }
  139. #endregion
  140. #region Implementation Details
  141. /// <summary>
  142. /// Creates a nested dynamic site which uses the unpacked arguments.
  143. /// </summary>
  144. internal DynamicMetaObject InvokeForeignObject(DynamicMetaObject target, DynamicMetaObject[] args) {
  145. // need to unpack any dict / list arguments...
  146. CallInfo callInfo;
  147. List<Expression> metaArgs;
  148. Expression test;
  149. BindingRestrictions restrictions;
  150. TranslateArguments(target, args, out callInfo, out metaArgs, out test, out restrictions);
  151. Debug.Assert(metaArgs.Count > 0);
  152. return BindingHelpers.AddDynamicTestAndDefer(
  153. this,
  154. new DynamicMetaObject(
  155. Expression.Dynamic(
  156. _context.CompatInvoke(callInfo),
  157. typeof(object),
  158. metaArgs.ToArray()
  159. ),
  160. restrictions.Merge(BindingRestrictionsHelpers.GetRuntimeTypeRestriction(target.Expression, target.GetLimitType()))
  161. ),
  162. args,
  163. new ValidationInfo(test)
  164. );
  165. }
  166. /// <summary>
  167. /// Translates our CallSignature into a DLR Argument list and gives the simple MetaObject's which are extracted
  168. /// from the tuple or dictionary parameters being splatted.
  169. /// </summary>
  170. private void TranslateArguments(DynamicMetaObject target, DynamicMetaObject/*!*/[]/*!*/ args, out CallInfo /*!*/ callInfo, out List<Expression/*!*/>/*!*/ metaArgs, out Expression test, out BindingRestrictions restrictions) {
  171. Argument[] argInfo = _signature.GetArgumentInfos();
  172. List<string> namedArgNames = new List<string>();
  173. metaArgs = new List<Expression>();
  174. metaArgs.Add(target.Expression);
  175. Expression splatArgTest = null;
  176. Expression splatKwArgTest = null;
  177. restrictions = BindingRestrictions.Empty;
  178. for (int i = 0; i < argInfo.Length; i++) {
  179. Argument ai = argInfo[i];
  180. switch (ai.Kind) {
  181. case ArgumentType.Dictionary:
  182. PythonDictionary iac = (PythonDictionary)args[i].Value;
  183. List<string> argNames = new List<string>();
  184. foreach (KeyValuePair<object, object> kvp in iac) {
  185. string key = (string)kvp.Key;
  186. namedArgNames.Add(key);
  187. argNames.Add(key);
  188. metaArgs.Add(
  189. Expression.Call(
  190. AstUtils.Convert(args[i].Expression, typeof(PythonDictionary)),
  191. typeof(PythonDictionary).GetMethod("get_Item", new[] { typeof(object) }),
  192. AstUtils.Constant(key)
  193. )
  194. );
  195. }
  196. restrictions = restrictions.Merge(BindingRestrictionsHelpers.GetRuntimeTypeRestriction(args[i].Expression, args[i].GetLimitType()));
  197. splatKwArgTest = Expression.Call(
  198. typeof(PythonOps).GetMethod("CheckDictionaryMembers"),
  199. AstUtils.Convert(args[i].Expression, typeof(PythonDictionary)),
  200. AstUtils.Constant(argNames.ToArray())
  201. );
  202. break;
  203. case ArgumentType.List:
  204. IList<object> splattedArgs = (IList<object>)args[i].Value;
  205. splatArgTest = Expression.Equal(
  206. Expression.Property(AstUtils.Convert(args[i].Expression, args[i].GetLimitType()), typeof(ICollection<object>).GetProperty("Count")),
  207. AstUtils.Constant(splattedArgs.Count)
  208. );
  209. for (int splattedArg = 0; splattedArg < splattedArgs.Count; splattedArg++) {
  210. metaArgs.Add(
  211. Expression.Call(
  212. AstUtils.Convert(args[i].Expression, typeof(IList<object>)),
  213. typeof(IList<object>).GetMethod("get_Item"),
  214. AstUtils.Constant(splattedArg)
  215. )
  216. );
  217. }
  218. restrictions = restrictions.Merge(BindingRestrictionsHelpers.GetRuntimeTypeRestriction(args[i].Expression, args[i].GetLimitType()));
  219. break;
  220. case ArgumentType.Named:
  221. namedArgNames.Add(ai.Name);
  222. metaArgs.Add(args[i].Expression);
  223. break;
  224. case ArgumentType.Simple:
  225. metaArgs.Add(args[i].Expression);
  226. break;
  227. default:
  228. throw new InvalidOperationException();
  229. }
  230. }
  231. callInfo = new CallInfo(metaArgs.Count - 1, namedArgNames.ToArray());
  232. test = splatArgTest;
  233. if (splatKwArgTest != null) {
  234. if (test != null) {
  235. test = Expression.AndAlso(test, splatKwArgTest);
  236. } else {
  237. test = splatKwArgTest;
  238. }
  239. }
  240. }
  241. #endregion
  242. #region IPythonSite Members
  243. public PythonContext Context {
  244. get { return _context; }
  245. }
  246. #endregion
  247. #region IExpressionSerializable Members
  248. public virtual Expression CreateExpression() {
  249. return Expression.Call(
  250. typeof(PythonOps).GetMethod("MakeInvokeAction"),
  251. BindingHelpers.CreateBinderStateExpression(),
  252. Signature.CreateExpression()
  253. );
  254. }
  255. #endregion
  256. }
  257. }