PageRenderTime 40ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/IronPython_2_0/Src/IronPython/Runtime/Binding/SlotOrFunction.cs

#
C# | 340 lines | 264 code | 40 blank | 36 comment | 63 complexity | be8b92fcd52f21d669c807fdadd80a3e MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception, CPL-1.0, CC-BY-SA-3.0, BSD-3-Clause, ISC, AGPL-3.0, LGPL-2.1, Apache-2.0
  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. using System; using Microsoft;
  16. using System.Collections.Generic;
  17. using System.Diagnostics;
  18. using Microsoft.Linq.Expressions;
  19. using System.Reflection;
  20. using Microsoft.Scripting.Actions;
  21. using IronPython.Runtime.Types;
  22. using Microsoft.Scripting;
  23. using Microsoft.Scripting.Actions.Calls;
  24. using Microsoft.Scripting.Generation;
  25. using Microsoft.Scripting.Utils;
  26. namespace IronPython.Runtime.Binding {
  27. using Ast = Microsoft.Linq.Expressions.Expression;
  28. /// <summary>
  29. /// Provides an abstraction for calling something which might be a builtin function or
  30. /// might be some arbitrary user defined slot. If the object is a builtin function the
  31. /// call will go directly to the underlying .NET method. If the object is an arbitrary
  32. /// callable object we will setup a nested dynamic site for performing the additional
  33. /// dispatch.
  34. ///
  35. /// TODO: Wwe could probably do a specific binding to the object if it's another IDyanmicObject.
  36. /// </summary>
  37. sealed class SlotOrFunction {
  38. private readonly BindingTarget _function;
  39. private readonly MetaObject/*!*/ _target;
  40. public static readonly SlotOrFunction/*!*/ Empty = new SlotOrFunction(new MetaObject(Ast.Empty(), Restrictions.Empty));
  41. private SlotOrFunction() {
  42. }
  43. public SlotOrFunction(BindingTarget/*!*/ function, MetaObject/*!*/ target) {
  44. _target = target;
  45. _function = function;
  46. }
  47. public SlotOrFunction(MetaObject/*!*/ target) {
  48. _target = target;
  49. }
  50. public NarrowingLevel NarrowingLevel {
  51. get {
  52. if (_function != null) {
  53. return _function.NarrowingLevel;
  54. }
  55. return NarrowingLevel.None;
  56. }
  57. }
  58. public Type/*!*/ ReturnType {
  59. get {
  60. return _target.LimitType;
  61. }
  62. }
  63. public bool MaybeNotImplemented {
  64. get {
  65. if (_function != null) {
  66. MethodInfo mi = _function.Method as MethodInfo;
  67. if (mi != null) {
  68. return mi.ReturnTypeCustomAttributes.IsDefined(typeof(MaybeNotImplementedAttribute), false);
  69. }
  70. return false;
  71. }
  72. return true;
  73. }
  74. }
  75. public bool Success {
  76. get {
  77. if (_function != null) {
  78. return _function.Success;
  79. }
  80. return this != Empty;
  81. }
  82. }
  83. public MetaObject/*!*/ Target {
  84. get {
  85. return _target;
  86. }
  87. }
  88. /// <summary>
  89. /// Combines two methods, which came from two different binary types, selecting the method which has the best
  90. /// set of conversions (the conversions which result in the least narrowing).
  91. /// </summary>
  92. public static bool GetCombinedTargets(SlotOrFunction fCand, SlotOrFunction rCand, out SlotOrFunction fTarget, out SlotOrFunction rTarget) {
  93. fTarget = rTarget = Empty;
  94. if (fCand.Success) {
  95. if (rCand.Success) {
  96. if (fCand.NarrowingLevel <= rCand.NarrowingLevel) {
  97. fTarget = fCand;
  98. rTarget = rCand;
  99. } else {
  100. fTarget = Empty;
  101. rTarget = rCand;
  102. }
  103. } else {
  104. fTarget = fCand;
  105. }
  106. } else if (rCand.Success) {
  107. rTarget = rCand;
  108. } else {
  109. return false;
  110. }
  111. return true;
  112. }
  113. public static SlotOrFunction/*!*/ GetSlotOrFunction(BinderState/*!*/ state, SymbolId op, params MetaObject[] types) {
  114. PythonTypeSlot slot;
  115. SlotOrFunction res;
  116. if (TryGetBinder(state, types, op, SymbolId.Empty, out res)) {
  117. if (res != SlotOrFunction.Empty) {
  118. return res;
  119. }
  120. } else if (MetaUserObject.GetPythonType(types[0]).TryResolveSlot(state.Context, op, out slot)) {
  121. ParameterExpression tmp = Ast.Variable(typeof(object), "slotVal");
  122. Expression[] args = new Expression[types.Length - 1];
  123. for (int i = 1; i < types.Length; i++) {
  124. args[i - 1] = types[i].Expression;
  125. }
  126. return new SlotOrFunction(
  127. new MetaObject(
  128. Ast.Scope(
  129. Ast.Comma(
  130. MetaPythonObject.MakeTryGetTypeMember(
  131. state,
  132. slot,
  133. tmp,
  134. types[0].Expression,
  135. Ast.Call(
  136. typeof(DynamicHelpers).GetMethod("GetPythonType"),
  137. types[0].Expression
  138. )
  139. ),
  140. Ast.Dynamic(
  141. new InvokeBinder(
  142. state,
  143. new CallSignature(args.Length)
  144. ),
  145. typeof(object),
  146. ArrayUtils.Insert<Expression>(
  147. Ast.Constant(state.Context),
  148. tmp,
  149. args
  150. )
  151. )
  152. ),
  153. tmp
  154. ),
  155. Restrictions.Combine(types).Merge(Restrictions.TypeRestriction(types[0].Expression, types[0].LimitType))
  156. )
  157. );
  158. }
  159. return SlotOrFunction.Empty;
  160. }
  161. internal static bool TryGetBinder(BinderState/*!*/ state, MetaObject/*!*/[]/*!*/ types, SymbolId op, SymbolId rop, out SlotOrFunction/*!*/ res) {
  162. PythonType declType;
  163. return TryGetBinder(state, types, op, rop, out res, out declType);
  164. }
  165. /// <summary>
  166. /// Trys to geta MethodBinder associated the slot for the specified type.
  167. ///
  168. /// If a method is found the binder is set and true is returned.
  169. /// If nothing is found binder is null and true is returned.
  170. /// If something other than a method is found false is returned.
  171. ///
  172. /// TODO: Remove rop
  173. /// </summary>
  174. internal static bool TryGetBinder(BinderState/*!*/ state, MetaObject/*!*/[]/*!*/ types, SymbolId op, SymbolId rop, out SlotOrFunction/*!*/ res, out PythonType declaringType) {
  175. declaringType = null;
  176. MetaObject xType = types[0];
  177. BuiltinFunction xBf;
  178. if (!BindingHelpers.TryGetStaticFunction(state, op, xType, out xBf)) {
  179. res = SlotOrFunction.Empty;
  180. return false;
  181. }
  182. xBf = CheckAlwaysNotImplemented(xBf);
  183. BindingTarget bt;
  184. MetaObject binder;
  185. MetaObject yType = null;
  186. BuiltinFunction yBf = null;
  187. if (types.Length > 1) {
  188. yType = types[1];
  189. if (!BindingHelpers.IsSubclassOf(xType, yType) && !BindingHelpers.TryGetStaticFunction(state, rop, yType, out yBf)) {
  190. res = SlotOrFunction.Empty;
  191. return false;
  192. }
  193. yBf = CheckAlwaysNotImplemented(yBf);
  194. }
  195. if (yBf == xBf) {
  196. yBf = null;
  197. } else if (yBf != null && BindingHelpers.IsSubclassOf(yType, xType)) {
  198. xBf = null;
  199. }
  200. var mc = new ParameterBinderWithCodeContext(state.Binder, Ast.Constant(state.Context));
  201. if (xBf == null) {
  202. if (yBf == null) {
  203. binder = null;
  204. bt = null;
  205. } else {
  206. declaringType = DynamicHelpers.GetPythonTypeFromType(yBf.DeclaringType);
  207. binder = state.Binder.CallMethod(
  208. mc,
  209. yBf.Targets,
  210. types,
  211. new CallSignature(types.Length),
  212. Restrictions.Empty,
  213. PythonNarrowing.None,
  214. PythonNarrowing.BinaryOperator,
  215. out bt
  216. );
  217. }
  218. } else {
  219. if (yBf == null) {
  220. declaringType = DynamicHelpers.GetPythonTypeFromType(xBf.DeclaringType);
  221. binder = state.Binder.CallMethod(
  222. mc,
  223. xBf.Targets,
  224. types,
  225. new CallSignature(types.Length),
  226. Restrictions.Empty,
  227. PythonNarrowing.None,
  228. PythonNarrowing.BinaryOperator,
  229. out bt
  230. );
  231. } else {
  232. List<MethodBase> targets = new List<MethodBase>();
  233. targets.AddRange(xBf.Targets);
  234. foreach (MethodBase mb in yBf.Targets) {
  235. if (!ContainsMethodSignature(targets, mb)) targets.Add(mb);
  236. }
  237. binder = state.Binder.CallMethod(
  238. mc,
  239. targets.ToArray(),
  240. types,
  241. new CallSignature(types.Length),
  242. Restrictions.Empty,
  243. PythonNarrowing.None,
  244. PythonNarrowing.BinaryOperator,
  245. out bt
  246. );
  247. foreach (MethodBase mb in yBf.Targets) {
  248. if (bt.Method == mb) {
  249. declaringType = DynamicHelpers.GetPythonTypeFromType(yBf.DeclaringType);
  250. break;
  251. }
  252. }
  253. if (declaringType == null) {
  254. declaringType = DynamicHelpers.GetPythonTypeFromType(xBf.DeclaringType);
  255. }
  256. }
  257. }
  258. if (binder != null) {
  259. res = new SlotOrFunction(bt, binder);
  260. } else {
  261. res = SlotOrFunction.Empty;
  262. }
  263. Debug.Assert(res != null);
  264. return true;
  265. }
  266. private static BuiltinFunction CheckAlwaysNotImplemented(BuiltinFunction xBf) {
  267. if (xBf != null) {
  268. bool returnsValue = false;
  269. foreach (MethodBase mb in xBf.Targets) {
  270. if (CompilerHelpers.GetReturnType(mb) != typeof(NotImplementedType)) {
  271. returnsValue = true;
  272. break;
  273. }
  274. }
  275. if (!returnsValue) {
  276. xBf = null;
  277. }
  278. }
  279. return xBf;
  280. }
  281. private static bool ContainsMethodSignature(IList<MethodBase/*!*/>/*!*/ existing, MethodBase/*!*/ check) {
  282. ParameterInfo[] pis = check.GetParameters();
  283. foreach (MethodBase mb in existing) {
  284. if (MatchesMethodSignature(pis, mb)) return true;
  285. }
  286. return false;
  287. }
  288. private static bool MatchesMethodSignature(ParameterInfo/*!*/[]/*!*/ pis, MethodBase/*!*/ mb) {
  289. ParameterInfo[] pis1 = mb.GetParameters();
  290. if (pis.Length == pis1.Length) {
  291. for (int i = 0; i < pis.Length; i++) {
  292. if (pis[i].ParameterType != pis1[i].ParameterType) return false;
  293. }
  294. return true;
  295. } else {
  296. return false;
  297. }
  298. }
  299. }
  300. }