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

/DICK.B1/IronPython/Runtime/Types/BuiltinFunction.cs

https://bitbucket.org/williamybs/uidipythontool
C# | 1256 lines | 910 code | 207 blank | 139 comment | 195 complexity | 9e7bae568608e3d88a92da81bd0718ba MD5 | raw file

Large files files are truncated, but you can click here to view the full 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;
  22. using System.Collections.Generic;
  23. using System.Diagnostics;
  24. using System.Reflection;
  25. using System.Runtime.CompilerServices;
  26. using System.Dynamic;
  27. using System.Text;
  28. using System.Threading;
  29. using Microsoft.Scripting;
  30. using Microsoft.Scripting.Actions;
  31. using Microsoft.Scripting.Actions.Calls;
  32. using Microsoft.Scripting.Generation;
  33. using Microsoft.Scripting.Runtime;
  34. using Microsoft.Scripting.Utils;
  35. using IronPython.Runtime.Binding;
  36. using IronPython.Runtime.Operations;
  37. namespace IronPython.Runtime.Types {
  38. using Ast = Expression;
  39. using AstUtils = Microsoft.Scripting.Ast.Utils;
  40. /// <summary>
  41. /// BuiltinFunction represents any standard CLR function exposed to Python.
  42. /// This is used for both methods on standard Python types such as list or tuple
  43. /// and for methods from arbitrary .NET assemblies.
  44. ///
  45. /// All calls are made through the optimizedTarget which is created lazily.
  46. ///
  47. /// TODO: Back BuiltinFunction's by MethodGroup's.
  48. /// </summary>
  49. [PythonType("builtin_function_or_method"), DontMapGetMemberNamesToDir]
  50. public partial class BuiltinFunction : PythonTypeSlot, ICodeFormattable, IDynamicMetaObjectProvider, IDelegateConvertible, IFastInvokable {
  51. internal readonly BuiltinFunctionData/*!*/ _data; // information describing the BuiltinFunction
  52. internal readonly object _instance; // the bound instance or null if unbound
  53. private static readonly object _noInstance = new object();
  54. #region Static factories
  55. /// <summary>
  56. /// Creates a new builtin function for a static .NET function. This is used for module methods
  57. /// and well-known __new__ methods.
  58. /// </summary>
  59. internal static BuiltinFunction/*!*/ MakeFunction(string name, MethodBase[] infos, Type declaringType) {
  60. #if DEBUG
  61. foreach (MethodBase mi in infos) {
  62. Debug.Assert(!mi.ContainsGenericParameters);
  63. }
  64. #endif
  65. return new BuiltinFunction(name, infos, declaringType, FunctionType.AlwaysVisible | FunctionType.Function);
  66. }
  67. /// <summary>
  68. /// Creates a built-in function for a .NET method declared on a type.
  69. /// </summary>
  70. internal static BuiltinFunction/*!*/ MakeMethod(string name, MethodBase[] infos, Type declaringType, FunctionType ft) {
  71. foreach (MethodBase mi in infos) {
  72. if (mi.ContainsGenericParameters) {
  73. return new GenericBuiltinFunction(name, infos, declaringType, ft);
  74. }
  75. }
  76. return new BuiltinFunction(name, infos, declaringType, ft);
  77. }
  78. internal virtual BuiltinFunction/*!*/ BindToInstance(object instance) {
  79. return new BuiltinFunction(instance, _data);
  80. }
  81. #endregion
  82. #region Constructors
  83. internal BuiltinFunction(string/*!*/ name, MethodBase/*!*/[]/*!*/ originalTargets, Type/*!*/ declaringType, FunctionType functionType) {
  84. Assert.NotNull(name);
  85. Assert.NotNull(declaringType);
  86. Assert.NotNullItems(originalTargets);
  87. _data = new BuiltinFunctionData(name, originalTargets, declaringType, functionType);
  88. _instance = _noInstance;
  89. }
  90. /// <summary>
  91. /// Creates a bound built-in function. The instance may be null for built-in functions
  92. /// accessed for None.
  93. /// </summary>
  94. internal BuiltinFunction(object instance, BuiltinFunctionData/*!*/ data) {
  95. Assert.NotNull(data);
  96. _instance = instance;
  97. _data = data;
  98. }
  99. #endregion
  100. #region Internal API Surface
  101. internal void AddMethod(MethodInfo mi) {
  102. _data.AddMethod(mi);
  103. }
  104. internal bool TestData(object data) {
  105. return _data == data;
  106. }
  107. internal bool IsUnbound {
  108. get {
  109. return _instance == _noInstance;
  110. }
  111. }
  112. internal string Name {
  113. get {
  114. return _data.Name;
  115. }
  116. set {
  117. _data.Name = value;
  118. }
  119. }
  120. internal object Call(CodeContext context, SiteLocalStorage<CallSite<Func<CallSite, CodeContext, object, object[], object>>> storage, object instance, object[] args) {
  121. storage = GetInitializedStorage(context, storage);
  122. object callable;
  123. if (!GetDescriptor().TryGetValue(context, instance, DynamicHelpers.GetPythonTypeFromType(DeclaringType), out callable)) {
  124. callable = this;
  125. }
  126. return storage.Data.Target(storage.Data, context, callable, args);
  127. }
  128. internal object Call0(CodeContext context, SiteLocalStorage<CallSite<Func<CallSite, CodeContext, object, object>>> storage, object instance) {
  129. storage = GetInitializedStorage(context, storage);
  130. object callable;
  131. if (!GetDescriptor().TryGetValue(context, instance, DynamicHelpers.GetPythonTypeFromType(DeclaringType), out callable)) {
  132. callable = this;
  133. }
  134. return storage.Data.Target(storage.Data, context, callable);
  135. }
  136. private static SiteLocalStorage<CallSite<Func<CallSite, CodeContext, object, object[], object>>> GetInitializedStorage(CodeContext context, SiteLocalStorage<CallSite<Func<CallSite, CodeContext, object, object[], object>>> storage) {
  137. if (storage == null) {
  138. storage = PythonContext.GetContext(context).GetGenericCallSiteStorage();
  139. }
  140. if (storage.Data == null) {
  141. storage.Data = PythonContext.GetContext(context).MakeSplatSite();
  142. }
  143. return storage;
  144. }
  145. private static SiteLocalStorage<CallSite<Func<CallSite, CodeContext, object, object>>> GetInitializedStorage(CodeContext context, SiteLocalStorage<CallSite<Func<CallSite, CodeContext, object, object>>> storage) {
  146. if (storage.Data == null) {
  147. storage.Data = CallSite<Func<CallSite, CodeContext, object, object>>.Create(
  148. PythonContext.GetContext(context).InvokeNone
  149. );
  150. }
  151. return storage;
  152. }
  153. internal object Call(CodeContext context, SiteLocalStorage<CallSite<Func<CallSite, CodeContext, object, object[], IDictionary<object, object>, object>>> storage, object instance, object[] args, IDictionary<object, object> keywordArgs) {
  154. if (storage == null) {
  155. storage = PythonContext.GetContext(context).GetGenericKeywordCallSiteStorage();
  156. }
  157. if (storage.Data == null) {
  158. storage.Data = PythonContext.GetContext(context).MakeKeywordSplatSite();
  159. }
  160. if (instance != null) {
  161. return storage.Data.Target(storage.Data, context, this, ArrayUtils.Insert(instance, args), keywordArgs);
  162. }
  163. return storage.Data.Target(storage.Data, context, this, args, keywordArgs);
  164. }
  165. /// <summary>
  166. /// Returns a BuiltinFunction bound to the provided type arguments. Returns null if the binding
  167. /// cannot be performed.
  168. /// </summary>
  169. internal BuiltinFunction MakeGenericMethod(Type[] types) {
  170. TypeList tl = new TypeList(types);
  171. // check for cached method first...
  172. BuiltinFunction bf;
  173. if (_data.BoundGenerics != null) {
  174. lock (_data.BoundGenerics) {
  175. if (_data.BoundGenerics.TryGetValue(tl, out bf)) {
  176. return bf;
  177. }
  178. }
  179. }
  180. // Search for generic targets with the correct arity (number of type parameters).
  181. // Compatible targets must be MethodInfos by definition (constructors never take
  182. // type arguments).
  183. List<MethodBase> targets = new List<MethodBase>(Targets.Count);
  184. foreach (MethodBase mb in Targets) {
  185. MethodInfo mi = mb as MethodInfo;
  186. if (mi == null)
  187. continue;
  188. if (mi.ContainsGenericParameters && mi.GetGenericArguments().Length == types.Length)
  189. targets.Add(mi.MakeGenericMethod(types));
  190. }
  191. if (targets.Count == 0) {
  192. return null;
  193. }
  194. // Build a new ReflectedMethod that will contain targets with bound type arguments & cache it.
  195. bf = new BuiltinFunction(Name, targets.ToArray(), DeclaringType, FunctionType);
  196. EnsureBoundGenericDict();
  197. lock (_data.BoundGenerics) {
  198. _data.BoundGenerics[tl] = bf;
  199. }
  200. return bf;
  201. }
  202. /// <summary>
  203. /// Returns a descriptor for the built-in function if one is
  204. /// neededed
  205. /// </summary>
  206. internal PythonTypeSlot/*!*/ GetDescriptor() {
  207. if ((FunctionType & FunctionType.Method) != 0) {
  208. return new BuiltinMethodDescriptor(this);
  209. }
  210. return this;
  211. }
  212. public Type DeclaringType {
  213. [PythonHidden]
  214. get {
  215. return _data.DeclaringType;
  216. }
  217. }
  218. /// <summary>
  219. /// Gets the target methods that we'll be calling.
  220. /// </summary>
  221. public IList<MethodBase> Targets {
  222. [PythonHidden]
  223. get {
  224. return _data.Targets;
  225. }
  226. }
  227. /// <summary>
  228. /// True if the method should be visible to non-CLS opt-in callers
  229. /// </summary>
  230. internal override bool IsAlwaysVisible {
  231. get {
  232. return (_data.Type & FunctionType.AlwaysVisible) != 0;
  233. }
  234. }
  235. internal bool IsReversedOperator {
  236. get {
  237. return (FunctionType & FunctionType.ReversedOperator) != 0;
  238. }
  239. }
  240. internal bool IsBinaryOperator {
  241. get {
  242. return (FunctionType & FunctionType.BinaryOperator) != 0;
  243. }
  244. }
  245. internal FunctionType FunctionType {
  246. get {
  247. return _data.Type;
  248. }
  249. set {
  250. _data.Type = value;
  251. }
  252. }
  253. /// <summary>
  254. /// Makes a test for the built-in function against the private _data
  255. /// which is unique per built-in function.
  256. /// </summary>
  257. internal Expression/*!*/ MakeBoundFunctionTest(Expression/*!*/ functionTarget) {
  258. Debug.Assert(functionTarget.Type == typeof(BuiltinFunction));
  259. return Ast.Call(
  260. typeof(PythonOps).GetMethod("TestBoundBuiltinFunction"),
  261. functionTarget,
  262. AstUtils.Constant(_data, typeof(object))
  263. );
  264. }
  265. #endregion
  266. #region PythonTypeSlot Overrides
  267. internal override bool TryGetValue(CodeContext context, object instance, PythonType owner, out object value) {
  268. value = this;
  269. return true;
  270. }
  271. internal override bool GetAlwaysSucceeds {
  272. get {
  273. return true;
  274. }
  275. }
  276. internal override void MakeGetExpression(PythonBinder/*!*/ binder, Expression/*!*/ codeContext, DynamicMetaObject instance, DynamicMetaObject/*!*/ owner, ConditionalBuilder/*!*/ builder) {
  277. builder.FinishCondition(Ast.Constant(this));
  278. }
  279. #endregion
  280. #region ICodeFormattable members
  281. public string/*!*/ __repr__(CodeContext/*!*/ context) {
  282. if (IsUnbound) {
  283. return string.Format("<built-in function {0}>", Name);
  284. }
  285. return string.Format("<built-in method {0} of {1} object at {2}>",
  286. __name__,
  287. PythonOps.GetPythonTypeName(__self__),
  288. PythonOps.HexId(__self__));
  289. }
  290. #endregion
  291. #region IDynamicMetaObjectProvider Members
  292. DynamicMetaObject/*!*/ IDynamicMetaObjectProvider.GetMetaObject(Expression/*!*/ parameter) {
  293. return new Binding.MetaBuiltinFunction(parameter, BindingRestrictions.Empty, this);
  294. }
  295. internal class BindingResult {
  296. public readonly BindingTarget Target;
  297. public readonly DynamicMetaObject MetaObject;
  298. public readonly OptimizingCallDelegate Function;
  299. public BindingResult(BindingTarget target, DynamicMetaObject meta) {
  300. Target = target;
  301. MetaObject = meta;
  302. }
  303. public BindingResult(BindingTarget target, DynamicMetaObject meta, OptimizingCallDelegate function) {
  304. Target = target;
  305. MetaObject = meta;
  306. Function = function;
  307. }
  308. }
  309. /// <summary>
  310. /// Helper for generating the call to a builtin function. This is used for calls from built-in method
  311. /// descriptors and built-in functions w/ and w/o a bound instance.
  312. ///
  313. /// This provides all sorts of common checks on top of the call while the caller provides a delegate
  314. /// to do the actual call. The common checks include:
  315. /// check for generic-only methods
  316. /// reversed operator support
  317. /// transforming arguments so the default binder can understand them (currently user defined mapping types to PythonDictionary)
  318. /// returning NotImplemented from binary operators
  319. /// Warning when calling certain built-in functions
  320. ///
  321. /// </summary>
  322. /// <param name="call">The call binder we're doing the call for</param>
  323. /// <param name="codeContext">An expression which points to the code context</param>
  324. /// <param name="function">the meta object for the built in function</param>
  325. /// <param name="hasSelf">true if we're calling with an instance</param>
  326. /// <param name="args">The arguments being passed to the function</param>
  327. /// <param name="functionRestriction">A restriction for the built-in function, method desc, etc...</param>
  328. /// <param name="bind">A delegate to perform the actual call to the method.</param>
  329. internal DynamicMetaObject/*!*/ MakeBuiltinFunctionCall(DynamicMetaObjectBinder/*!*/ call, Expression/*!*/ codeContext, DynamicMetaObject/*!*/ function, DynamicMetaObject/*!*/[] args, bool hasSelf, BindingRestrictions/*!*/ functionRestriction, Func<DynamicMetaObject/*!*/[]/*!*/, BindingResult/*!*/> bind) {
  330. DynamicMetaObject res = null;
  331. // if we have a user defined operator for **args then transform it into a PythonDictionary
  332. DynamicMetaObject translated = TranslateArguments(call, codeContext, new DynamicMetaObject(function.Expression, functionRestriction, function.Value), args, hasSelf, Name);
  333. if (translated != null) {
  334. return translated;
  335. }
  336. // swap the arguments if we have a reversed operator
  337. if (IsReversedOperator) {
  338. ArrayUtils.SwapLastTwo(args);
  339. }
  340. // do the appropriate calling logic
  341. BindingResult result = bind(args);
  342. // validate the result
  343. BindingTarget target = result.Target;
  344. res = result.MetaObject;
  345. if (target.Overload != null && target.Overload.IsProtected) {
  346. // report an error when calling a protected member
  347. res = new DynamicMetaObject(
  348. BindingHelpers.TypeErrorForProtectedMember(
  349. target.Overload.DeclaringType,
  350. target.Overload.Name
  351. ),
  352. res.Restrictions
  353. );
  354. } else if (IsBinaryOperator && args.Length == 2 && IsThrowException(res.Expression)) {
  355. // Binary Operators return NotImplemented on failure.
  356. res = new DynamicMetaObject(
  357. Ast.Property(null, typeof(PythonOps), "NotImplemented"),
  358. res.Restrictions
  359. );
  360. } else if (target.Overload != null) {
  361. // Add profiling information for this builtin function, if applicable
  362. IPythonSite pythonSite = (call as IPythonSite);
  363. if (pythonSite != null) {
  364. var pc = pythonSite.Context;
  365. var po = pc.Options as PythonOptions;
  366. if (po != null && po.EnableProfiler) {
  367. Profiler profiler = Profiler.GetProfiler(pc);
  368. res = new DynamicMetaObject(
  369. profiler.AddProfiling(res.Expression, target.Overload.ReflectionInfo),
  370. res.Restrictions
  371. );
  372. }
  373. }
  374. }
  375. // add any warnings that are applicable for calling this function
  376. WarningInfo info;
  377. if (target.Overload != null && BindingWarnings.ShouldWarn(PythonContext.GetPythonContext(call), target.Overload, out info)) {
  378. res = info.AddWarning(codeContext, res);
  379. }
  380. // finally add the restrictions for the built-in function and return the result.
  381. res = new DynamicMetaObject(
  382. res.Expression,
  383. functionRestriction.Merge(res.Restrictions)
  384. );
  385. // The function can return something typed to boolean or int.
  386. // If that happens, we need to apply Python's boxing rules.
  387. if (res.Expression.Type.IsValueType) {
  388. res = BindingHelpers.AddPythonBoxing(res);
  389. } else if (res.Expression.Type == typeof(void)) {
  390. res = new DynamicMetaObject(
  391. Expression.Block(
  392. res.Expression,
  393. Expression.Constant(null)
  394. ),
  395. res.Restrictions
  396. );
  397. }
  398. return res;
  399. }
  400. internal static DynamicMetaObject TranslateArguments(DynamicMetaObjectBinder call, Expression codeContext, DynamicMetaObject function, DynamicMetaObject/*!*/[] args, bool hasSelf, string name) {
  401. if (hasSelf) {
  402. args = ArrayUtils.RemoveFirst(args);
  403. }
  404. CallSignature sig = BindingHelpers.GetCallSignature(call);
  405. if (sig.HasDictionaryArgument()) {
  406. int index = sig.IndexOf(ArgumentType.Dictionary);
  407. DynamicMetaObject dict = args[index];
  408. if (!(dict.Value is IDictionary) && dict.Value != null) {
  409. // The DefaultBinder only handles types that implement IDictionary. Here we have an
  410. // arbitrary user-defined mapping type. We'll convert it into a PythonDictionary
  411. // and then have an embedded dynamic site pass that dictionary through to the default
  412. // binder.
  413. DynamicMetaObject[] dynamicArgs = ArrayUtils.Insert(function, args);
  414. dynamicArgs[index + 1] = new DynamicMetaObject(
  415. Expression.Call(
  416. typeof(PythonOps).GetMethod("UserMappingToPythonDictionary"),
  417. codeContext,
  418. args[index].Expression,
  419. AstUtils.Constant(name)
  420. ),
  421. BindingRestrictionsHelpers.GetRuntimeTypeRestriction(dict.Expression, dict.GetLimitType()),
  422. PythonOps.UserMappingToPythonDictionary(PythonContext.GetPythonContext(call).SharedContext, dict.Value, name)
  423. );
  424. if (call is IPythonSite) {
  425. dynamicArgs = ArrayUtils.Insert(
  426. new DynamicMetaObject(codeContext, BindingRestrictions.Empty),
  427. dynamicArgs
  428. );
  429. }
  430. return new DynamicMetaObject(
  431. Ast.Dynamic(
  432. call,
  433. typeof(object),
  434. DynamicUtils.GetExpressions(dynamicArgs)
  435. ),
  436. BindingRestrictions.Combine(dynamicArgs).Merge(BindingRestrictionsHelpers.GetRuntimeTypeRestriction(dict.Expression, dict.GetLimitType()))
  437. );
  438. }
  439. }
  440. if (sig.HasListArgument()) {
  441. int index = sig.IndexOf(ArgumentType.List);
  442. DynamicMetaObject str = args[index];
  443. // TODO: ANything w/ __iter__ that's not an IList<object>
  444. if (!(str.Value is IList<object>) && str.Value is IEnumerable) {
  445. // The DefaultBinder only handles types that implement IList<object>. Here we have a
  446. // string. We'll convert it into a tuple
  447. // and then have an embedded dynamic site pass that tuple through to the default
  448. // binder.
  449. DynamicMetaObject[] dynamicArgs = ArrayUtils.Insert(function, args);
  450. dynamicArgs[index + 1] = new DynamicMetaObject(
  451. Expression.Call(
  452. typeof(PythonOps).GetMethod("MakeTupleFromSequence"),
  453. Expression.Convert(args[index].Expression, typeof(object))
  454. ),
  455. BindingRestrictions.Empty
  456. );
  457. if (call is IPythonSite) {
  458. dynamicArgs = ArrayUtils.Insert(
  459. new DynamicMetaObject(codeContext, BindingRestrictions.Empty),
  460. dynamicArgs
  461. );
  462. }
  463. return new DynamicMetaObject(
  464. Ast.Dynamic(
  465. call,
  466. typeof(object),
  467. DynamicUtils.GetExpressions(dynamicArgs)
  468. ),
  469. function.Restrictions.Merge(
  470. BindingRestrictions.Combine(args).Merge(BindingRestrictionsHelpers.GetRuntimeTypeRestriction(str.Expression, str.GetLimitType()))
  471. )
  472. );
  473. }
  474. }
  475. return null;
  476. }
  477. private static bool IsThrowException(Expression expr) {
  478. if (expr.NodeType == ExpressionType.Throw) {
  479. return true;
  480. } else if (expr.NodeType == ExpressionType.Convert) {
  481. return IsThrowException(((UnaryExpression)expr).Operand);
  482. } else if (expr.NodeType == ExpressionType.Block) {
  483. foreach (Expression e in ((BlockExpression)expr).Expressions) {
  484. if (IsThrowException(e)) {
  485. return true;
  486. }
  487. }
  488. }
  489. return false;
  490. }
  491. internal KeyValuePair<OptimizingCallDelegate, Type[]> MakeBuiltinFunctionDelegate(DynamicMetaObjectBinder/*!*/ call, Expression/*!*/ codeContext, DynamicMetaObject/*!*/[] args, bool hasSelf, Func<DynamicMetaObject/*!*/[]/*!*/, BindingResult/*!*/> bind) {
  492. OptimizingCallDelegate res = null;
  493. // swap the arguments if we have a reversed operator
  494. if (IsReversedOperator) {
  495. ArrayUtils.SwapLastTwo(args);
  496. }
  497. // if we have a user defined operator for **args then transform it into a PythonDictionary (not supported here)
  498. Debug.Assert(!BindingHelpers.GetCallSignature(call).HasDictionaryArgument());
  499. // do the appropriate calling logic
  500. BindingResult result = bind(args);
  501. // validate the result
  502. BindingTarget target = result.Target;
  503. res = result.Function;
  504. if (target.Overload != null && target.Overload.IsProtected) {
  505. Type declaringType = target.Overload.DeclaringType;
  506. string methodName = target.Overload.Name;
  507. string name = target.Overload.Name;
  508. // report an error when calling a protected member
  509. res = delegate(object[] callArgs, out bool shouldOptimize) {
  510. shouldOptimize = false;
  511. throw PythonOps.TypeErrorForProtectedMember(declaringType, name);
  512. };
  513. } else if (result.MetaObject.Expression.NodeType == ExpressionType.Throw) {
  514. if (IsBinaryOperator && args.Length == 2) {
  515. // Binary Operators return NotImplemented on failure.
  516. res = delegate(object[] callArgs, out bool shouldOptimize) {
  517. shouldOptimize = false;
  518. return PythonOps.NotImplemented;
  519. };
  520. }
  521. // error, we don't optimize this yet.
  522. return new KeyValuePair<OptimizingCallDelegate, Type[]>(null, null);
  523. }
  524. if (res == null) {
  525. return new KeyValuePair<OptimizingCallDelegate, Type[]>();
  526. }
  527. // add any warnings that are applicable for calling this function
  528. WarningInfo info;
  529. if (target.Overload != null && BindingWarnings.ShouldWarn(PythonContext.GetPythonContext(call), target.Overload, out info)) {
  530. res = info.AddWarning(res);
  531. }
  532. // finally add the restrictions for the built-in function and return the result.
  533. Type[] typeRestrictions = ArrayUtils.MakeArray(result.Target.RestrictedArguments.GetTypes());
  534. // The function can return something typed to boolean or int.
  535. // If that happens, we need to apply Python's boxing rules.
  536. var returnType = result.Target.Overload.ReturnType;
  537. if (returnType == typeof(bool)) {
  538. var tmp = res;
  539. res = delegate(object[] callArgs, out bool shouldOptimize) {
  540. return ScriptingRuntimeHelpers.BooleanToObject((bool)tmp(callArgs, out shouldOptimize));
  541. };
  542. } else if (returnType == typeof(int)) {
  543. var tmp = res;
  544. res = delegate(object[] callArgs, out bool shouldOptimize) {
  545. return ScriptingRuntimeHelpers.Int32ToObject((int)tmp(callArgs, out shouldOptimize));
  546. };
  547. }
  548. if (IsReversedOperator) {
  549. var tmp = res;
  550. // add the swapping code for the caller
  551. res = delegate(object[] callArgs, out bool shouldOptimize) {
  552. ArrayUtils.SwapLastTwo(callArgs);
  553. return tmp(callArgs, out shouldOptimize);
  554. };
  555. // and update the type restrictions
  556. if (typeRestrictions != null) {
  557. ArrayUtils.SwapLastTwo(typeRestrictions);
  558. }
  559. }
  560. var pc = PythonContext.GetContext(PythonContext.GetPythonContext(call).SharedContext);
  561. var po = pc.Options as PythonOptions;
  562. if (po != null && po.EnableProfiler) {
  563. Profiler profiler = Profiler.GetProfiler(pc);
  564. res = profiler.AddProfiling(res, target.Overload.ReflectionInfo);
  565. }
  566. return new KeyValuePair<OptimizingCallDelegate, Type[]>(res, typeRestrictions);
  567. }
  568. #endregion
  569. #region Public Python APIs
  570. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "cls")]
  571. public static object/*!*/ __new__(object cls, object newFunction, object inst) {
  572. return new Method(newFunction, inst, null);
  573. }
  574. public int __cmp__(CodeContext/*!*/ context, [NotNull]BuiltinFunction/*!*/ other) {
  575. if (other == this) {
  576. return 0;
  577. }
  578. if (!IsUnbound && !other.IsUnbound) {
  579. int result = PythonOps.Compare(__self__, other.__self__);
  580. if (result != 0) {
  581. return result;
  582. }
  583. if (_data == other._data) {
  584. return 0;
  585. }
  586. }
  587. int res = String.CompareOrdinal(__name__, other.__name__);
  588. if (res != 0) {
  589. return res;
  590. }
  591. res = String.CompareOrdinal(Get__module__(context), other.Get__module__(context));
  592. if (res != 0) {
  593. return res;
  594. }
  595. long lres = IdDispenser.GetId(this) - IdDispenser.GetId(other);
  596. return lres > 0 ? 1 : -1;
  597. }
  598. // these are present in CPython but always return NotImplemented.
  599. [return: MaybeNotImplemented]
  600. [Python3Warning("builtin_function_or_method order comparisons not supported in 3.x")]
  601. public static NotImplementedType operator >(BuiltinFunction self, BuiltinFunction other) {
  602. return PythonOps.NotImplemented;
  603. }
  604. [return: MaybeNotImplemented]
  605. [Python3Warning("builtin_function_or_method order comparisons not supported in 3.x")]
  606. public static NotImplementedType operator <(BuiltinFunction self, BuiltinFunction other) {
  607. return PythonOps.NotImplemented;
  608. }
  609. [return: MaybeNotImplemented]
  610. [Python3Warning("builtin_function_or_method order comparisons not supported in 3.x")]
  611. public static NotImplementedType operator >=(BuiltinFunction self, BuiltinFunction other) {
  612. return PythonOps.NotImplemented;
  613. }
  614. [return: MaybeNotImplemented]
  615. [Python3Warning("builtin_function_or_method order comparisons not supported in 3.x")]
  616. public static NotImplementedType operator <=(BuiltinFunction self, BuiltinFunction other) {
  617. return PythonOps.NotImplemented;
  618. }
  619. public int __hash__(CodeContext/*!*/ context) {
  620. return PythonOps.Hash(context, _instance) ^ PythonOps.Hash(context, _data);
  621. }
  622. [SpecialName, PropertyMethod]
  623. public string Get__module__(CodeContext/*!*/ context) {
  624. if (Targets.Count > 0) {
  625. PythonType declaringType = DynamicHelpers.GetPythonTypeFromType(DeclaringType);
  626. return PythonTypeOps.GetModuleName(context, declaringType.UnderlyingSystemType);
  627. }
  628. return null;
  629. }
  630. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic"), SpecialName, PropertyMethod]
  631. public void Set__module__(string value) {
  632. // Do nothing but don't return an error
  633. }
  634. /// <summary>
  635. /// Provides (for reflected methods) a mapping from a signature to the exact target
  636. /// which takes this signature.
  637. /// signature with syntax like the following:
  638. /// someClass.SomeMethod.Overloads[str, int]("Foo", 123)
  639. /// </summary>
  640. public virtual BuiltinFunctionOverloadMapper Overloads {
  641. [PythonHidden]
  642. get {
  643. // The mapping is actually provided by a class rather than a dictionary
  644. // since it's hard to generate all the keys of the signature mapping when
  645. // two type systems are involved.
  646. return new BuiltinFunctionOverloadMapper(this, IsUnbound ? null : _instance);
  647. }
  648. }
  649. /// <summary>
  650. /// Gets the overload dictionary for the logical function. These overloads
  651. /// are never bound to an instance.
  652. /// </summary>
  653. internal Dictionary<BuiltinFunction.TypeList, BuiltinFunction> OverloadDictionary {
  654. get {
  655. if (_data.OverloadDictionary == null) {
  656. Interlocked.CompareExchange(
  657. ref _data.OverloadDictionary,
  658. new Dictionary<BuiltinFunction.TypeList, BuiltinFunction>(),
  659. null);
  660. }
  661. return _data.OverloadDictionary;
  662. }
  663. }
  664. public string __name__ {
  665. get {
  666. return Name;
  667. }
  668. }
  669. public virtual string __doc__ {
  670. get {
  671. StringBuilder sb = new StringBuilder();
  672. IList<MethodBase> targets = Targets;
  673. for (int i = 0; i < targets.Count; i++) {
  674. if (targets[i] != null) {
  675. sb.Append(DocBuilder.DocOneInfo(targets[i], Name));
  676. }
  677. }
  678. return sb.ToString();
  679. }
  680. }
  681. public object __self__ {
  682. get {
  683. if (IsUnbound) {
  684. return null;
  685. }
  686. return _instance;
  687. }
  688. }
  689. public object __call__(CodeContext/*!*/ context, SiteLocalStorage<CallSite<Func<CallSite, CodeContext, object, object[], IDictionary<object, object>, object>>> storage, [ParamDictionary]IDictionary<object, object> dictArgs, params object[] args) {
  690. return Call(context, storage, null, args, dictArgs);
  691. }
  692. internal virtual bool IsOnlyGeneric {
  693. get {
  694. return false;
  695. }
  696. }
  697. #endregion
  698. #region Private members
  699. private BinderType BinderType {
  700. get {
  701. return IsBinaryOperator ? BinderType.BinaryOperator : BinderType.Normal;
  702. }
  703. }
  704. private void EnsureBoundGenericDict() {
  705. if (_data.BoundGenerics == null) {
  706. Interlocked.CompareExchange<Dictionary<TypeList, BuiltinFunction>>(
  707. ref _data.BoundGenerics,
  708. new Dictionary<TypeList, BuiltinFunction>(1),
  709. null);
  710. }
  711. }
  712. internal class TypeList {
  713. private Type[] _types;
  714. public TypeList(Type[] types) {
  715. Debug.Assert(types != null);
  716. _types = types;
  717. }
  718. public override bool Equals(object obj) {
  719. TypeList tl = obj as TypeList;
  720. if (tl == null || _types.Length != tl._types.Length) return false;
  721. for (int i = 0; i < _types.Length; i++) {
  722. if (_types[i] != tl._types[i]) return false;
  723. }
  724. return true;
  725. }
  726. public override int GetHashCode() {
  727. int hc = 6551;
  728. foreach (Type t in _types) {
  729. hc = (hc << 5) ^ t.GetHashCode();
  730. }
  731. return hc;
  732. }
  733. }
  734. #endregion
  735. #region IDelegateConvertible Members
  736. Delegate IDelegateConvertible.ConvertToDelegate(Type type) {
  737. // see if we have any functions which are compatible with the delegate type...
  738. ParameterInfo[] delegateParams = type.GetMethod("Invoke").GetParameters();
  739. // if we have overloads then we need to do the overload resolution at runtime
  740. if (Targets.Count == 1) {
  741. MethodInfo mi = Targets[0] as MethodInfo;
  742. if (mi != null) {
  743. ParameterInfo[] methodParams = mi.GetParameters();
  744. if (methodParams.Length == delegateParams.Length) {
  745. bool match = true;
  746. for (int i = 0; i < methodParams.Length; i++) {
  747. if (delegateParams[i].ParameterType != methodParams[i].ParameterType) {
  748. match = false;
  749. break;
  750. }
  751. }
  752. if (match) {
  753. if (IsUnbound) {
  754. return Delegate.CreateDelegate(type, mi);
  755. } else {
  756. return Delegate.CreateDelegate(type, _instance, mi);
  757. }
  758. }
  759. }
  760. }
  761. }
  762. return null;
  763. }
  764. #endregion
  765. #region BuiltinFunctionData
  766. internal sealed class BuiltinFunctionData {
  767. public string/*!*/ Name;
  768. public MethodBase/*!*/[]/*!*/ Targets;
  769. public readonly Type/*!*/ DeclaringType;
  770. public FunctionType Type;
  771. public Dictionary<TypeList, BuiltinFunction> BoundGenerics;
  772. public Dictionary<BuiltinFunction.TypeList, BuiltinFunction> OverloadDictionary;
  773. public Dictionary<CallKey, OptimizingInfo> FastCalls;
  774. public BuiltinFunctionData(string name, MethodBase[] targets, Type declType, FunctionType functionType) {
  775. Name = name;
  776. Targets = targets;
  777. DeclaringType = declType;
  778. Type = functionType;
  779. }
  780. internal void AddMethod(MethodBase/*!*/ info) {
  781. Assert.NotNull(info);
  782. MethodBase[] ni = new MethodBase[Targets.Length + 1];
  783. Targets.CopyTo(ni, 0);
  784. ni[Targets.Length] = info;
  785. Targets = ni;
  786. }
  787. }
  788. #endregion
  789. #region IFastInvokable Members
  790. internal class CallKey : IEquatable<CallKey> {
  791. public readonly Type DelegateType;
  792. public readonly CallSignature Signature;
  793. public readonly Type[] Types;
  794. public readonly Type SelfType;
  795. public bool IsUnbound;
  796. public CallKey(CallSignature signature, Type selfType, Type delegateType, Type[] types, bool isUnbound) {
  797. Signature = signature;
  798. Types = types;
  799. SelfType = selfType;
  800. IsUnbound = isUnbound;
  801. DelegateType = delegateType;
  802. }
  803. #region IEquatable<CallKey> Members
  804. public bool Equals(CallKey other) {
  805. if (DelegateType != other.DelegateType ||
  806. Signature != other.Signature ||
  807. IsUnbound != other.IsUnbound ||
  808. SelfType != other.SelfType ||
  809. Types.Length != other.Types.Length) {
  810. return false;
  811. }
  812. for (int i = 0; i < Types.Length; i++) {
  813. if (Types[i] != other.Types[i]) {
  814. return false;
  815. }
  816. }
  817. return true;
  818. }
  819. #endregion
  820. public override bool Equals(object obj) {
  821. CallKey other = obj as CallKey;
  822. if (other == null) {
  823. return false;
  824. }
  825. return Equals(other);
  826. }
  827. public override int GetHashCode() {
  828. int res = Signature.GetHashCode() ^ (IsUnbound ? 0x01 : 0x00);
  829. foreach (Type t in Types) {
  830. res ^= t.GetHashCode();
  831. }
  832. res ^= DelegateType.GetHashCode();
  833. if (SelfType != null) {
  834. res ^= SelfType.GetHashCode();
  835. }
  836. return res;
  837. }
  838. }
  839. FastBindResult<T> IFastInvokable.MakeInvokeBinding<T>(CallSite<T> site, PythonInvokeBinder binder, CodeContext state, object[] args) {
  840. if (binder.Signature.HasDictionaryArgument() ||
  841. binder.Signature.HasListArgument() ||
  842. !IsUnbound && __self__ is IStrongBox ||
  843. typeof(T).GetMethod("Invoke").ReturnType != typeof(object)) {
  844. return new FastBindResult<T>();
  845. }
  846. Type[] callTypes = CompilerHelpers.GetTypes(args);
  847. CallKey callKey = new CallKey(binder.Signature, IsUnbound ? null : CompilerHelpers.GetType(__self__), typeof(T), callTypes, IsUnbound);
  848. if (_data.FastCalls == null) {
  849. Interlocked.CompareExchange(
  850. ref _data.FastCalls,
  851. new Dictionary<CallKey, OptimizingInfo>(),
  852. null);
  853. }
  854. lock (_data.FastCalls) {
  855. OptimizingInfo optInfo = null;
  856. if (!_data.FastCalls.TryGetValue(callKey, out optInfo)) {
  857. KeyValuePair<OptimizingCallDelegate, Type[]> call = MakeCall<T>(binder, args);
  858. if (call.Key != null) {
  859. _data.FastCalls[callKey] = optInfo = new OptimizingInfo(call.Key, call.Value, binder.Signature);
  860. }
  861. }
  862. if (optInfo != null) {
  863. if (optInfo.ShouldOptimize) {
  864. // if we've already produced an optimal rule don't go and produce an
  865. // unoptimal rule (which will not hit any targets anyway - it's hit count
  866. // has been exceeded and now it always fails).
  867. return new FastBindResult<T>();
  868. }
  869. ParameterInfo[] prms = typeof(T).GetMethod("Invoke").GetParameters();
  870. Type[] allParams = ArrayUtils.ConvertAll(prms, (inp) => inp.ParameterType);
  871. allParams = ArrayUtils.Append(allParams, typeof(object));
  872. Type[] typeParams = new Type[prms.Length - 2]; // skip site, context
  873. for (int i = 2; i < prms.Length; i++) {
  874. typeParams[i - 2] = prms[i].ParameterType;
  875. }
  876. int argCount = typeParams.Length - 1;// no func
  877. Type callerType = GetCallerType(argCount);
  878. if (callerType != null) {
  879. callerType = callerType.MakeGenericType(typeParams);
  880. object[] createArgs = new object[argCount + 2 + (!IsUnbound ? 1 : 0)];
  881. createArgs[0] = optInfo;
  882. createArgs[1] = this;
  883. if (optInfo.TypeTest != null) {
  884. for (int i = 2; i < createArgs.Length; i++) {
  885. createArgs[i] = optInfo.TypeTest[i - 2];
  886. }
  887. }
  888. if (!IsUnbound) {
  889. // force a type test on self
  890. createArgs[2] = CompilerHelpers.GetType(__self__);
  891. }
  892. object fc = Activator.CreateInstance(callerType, createArgs);
  893. PerfTrack.NoteEvent(PerfTrack.Categories.BindingFast, "BuiltinFunction");
  894. return new FastBindResult<T>((T)(object)fc.GetType().GetField("MyDelegate").GetValue(fc), false);
  895. }
  896. }
  897. return new FastBindResult<T>();
  898. }
  899. }
  900. private KeyValuePair<OptimizingCallDelegate, Type[]> MakeCall<T>(PythonInvokeBinder binder, object[] args) where T : class {
  901. Expression codeContext = Expression.Parameter(typeof(CodeContext), "context");
  902. var pybinder = PythonContext.GetPythonContext(binder).Binder;
  903. KeyValuePair<OptimizingCallDelegate, Type[]> call;
  904. if (IsUnbound) {
  905. call = MakeBuiltinFunctionDelegate(
  906. binder,
  907. codeContext,
  908. GetMetaObjects<T>(args),
  909. false, // no self
  910. (newArgs) => {
  911. var resolver = new PythonOverloadResolver(
  912. pybinder,
  913. newArgs,
  914. BindingHelpers.GetCallSignature(binder),
  915. codeContext
  916. );
  917. BindingTarget target;
  918. DynamicMetaObject res = pybinder.CallMethod(
  919. resolver,
  920. Targets,
  921. BindingRestrictions.Empty,
  922. Name,
  923. PythonNarrowing.None,
  924. IsBinaryOperator ? PythonNarrowing.BinaryOperator : NarrowingLevel.All,
  925. out target
  926. );
  927. return new BuiltinFunction.BindingResult(target,
  928. BindingHelpers.AddPythonBoxing(res),
  929. target.Success ? target.MakeDelegate() : null);
  930. }
  931. );
  932. } else {
  933. DynamicMetaObject self = new DynamicMetaObject(Expression.Parameter(CompilerHelpers.GetType(__self__), "self"), BindingRestrictions.Empty, __self__);
  934. DynamicMetaObject[] metaArgs = GetMetaObjects<T>(args);
  935. call = MakeBuiltinFunctionDelegate(
  936. binder,
  937. codeContext,
  938. ArrayUtils.Insert(self, metaArgs),
  939. true,
  940. (newArgs) => {
  941. CallSignature signature = BindingHelpers.GetCallSignature(binder);
  942. DynamicMetaObject res;
  943. BindingTarget target;
  944. PythonOverloadResolver resolver;
  945. if (IsReversedOperator) {
  946. resolver = new PythonOverloadResolver(
  947. pybinder,
  948. newArgs,
  949. MetaBuiltinFunction.GetReversedSignature(signature),
  950. codeContext
  951. );
  952. } else {
  953. resolver = new PythonOverloadResolver(
  954. pybinder,
  955. self,
  956. metaArgs,
  957. signature,
  958. codeContext
  959. );
  960. }
  961. res = pybinder.CallMethod(
  962. resolver,
  963. Targets,
  964. self.Restrictions,
  965. Name,
  966. NarrowingLevel.None,
  967. IsBinaryOperator ? PythonNarrowing.BinaryOperator : NarrowingLevel.All,
  968. out target
  969. );
  970. return new BuiltinFunction.BindingResult(target, BindingHelpers.AddPythonBoxing(res), target.Success ? target.MakeDelegate() : null);
  971. }
  972. );
  973. }
  974. return call;
  975. }
  976. private static DynamicMetaObject[] GetMetaObjects<T>(object[] args) {
  977. ParameterInfo[] pis = typeof(T).GetMethod("Invoke").GetParameters();
  978. DynamicMetaObject[] res = new DynamicMetaObject[args.Length]; // remove CodeContext, func
  979. for (int i = 0; i < res.Length; i++) {
  980. res[i] = DynamicMetaObject.Create(
  981. args[i],
  982. Expression.Parameter(pis[i + 3].ParameterType, "arg" + i) // +3 == skipping callsite, codecontext, func
  983. );
  984. }
  985. return res;
  986. }
  987. #endregion
  988. }
  989. /// <summary>
  990. /// A custom built-in function which supports indexing
  991. /// </summary>
  992. public class GenericBuiltinFunction : BuiltinFunction {
  993. internal GenericBuiltinFunction(string/*!*/ name, MethodBase/*!*/[]/*!*/ originalTargets, Type/*!…

Large files files are truncated, but you can click here to view the full file