PageRenderTime 45ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/Languages/IronPython/IronPython/Runtime/Binding/PythonInvokeBinder.cs

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