PageRenderTime 59ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/Languages/IronPython/IronPython/Runtime/Binding/PythonProtocol.Operations.cs

http://github.com/IronLanguages/main
C# | 2473 lines | 1905 code | 321 blank | 247 comment | 377 complexity | 609cb2ac47e4f193abdcc048aff76d0d 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 System.Numerics;
  18. #else
  19. using Microsoft.Scripting.Math;
  20. using Complex = Microsoft.Scripting.Math.Complex64;
  21. #endif
  22. using System;
  23. using System.Collections;
  24. using System.Collections.Generic;
  25. using System.Diagnostics;
  26. using System.Dynamic;
  27. using System.Reflection;
  28. using System.Text;
  29. using Microsoft.Scripting;
  30. using Microsoft.Scripting.Actions;
  31. using Microsoft.Scripting.Actions.Calls;
  32. using Microsoft.Scripting.Ast;
  33. using Microsoft.Scripting.Generation;
  34. using Microsoft.Scripting.Runtime;
  35. using Microsoft.Scripting.Utils;
  36. using IronPython.Runtime.Operations;
  37. using IronPython.Runtime.Types;
  38. namespace IronPython.Runtime.Binding {
  39. using Ast = Expression;
  40. using AstUtils = Microsoft.Scripting.Ast.Utils;
  41. static partial class PythonProtocol {
  42. private const string DisallowCoerce = "DisallowCoerce";
  43. public static DynamicMetaObject/*!*/ Operation(BinaryOperationBinder/*!*/ operation, DynamicMetaObject target, DynamicMetaObject arg, DynamicMetaObject errorSuggestion) {
  44. PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Fallback BinaryOperator " + target.LimitType.FullName + " " + operation.Operation + " " + arg.LimitType.FullName);
  45. PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, operation.Operation.ToString());
  46. DynamicMetaObject[] args = new[] { target, arg };
  47. if (BindingHelpers.NeedsDeferral(args)) {
  48. return operation.Defer(target, arg);
  49. }
  50. ValidationInfo valInfo = BindingHelpers.GetValidationInfo(args);
  51. PythonOperationKind? pyOperator = null;
  52. switch (operation.Operation) {
  53. case ExpressionType.Add: pyOperator = PythonOperationKind.Add; break;
  54. case ExpressionType.And: pyOperator = PythonOperationKind.BitwiseAnd; break;
  55. case ExpressionType.Divide: pyOperator = PythonOperationKind.Divide; break;
  56. case ExpressionType.ExclusiveOr: pyOperator = PythonOperationKind.ExclusiveOr; break;
  57. case ExpressionType.Modulo: pyOperator = PythonOperationKind.Mod; break;
  58. case ExpressionType.Multiply: pyOperator = PythonOperationKind.Multiply; break;
  59. case ExpressionType.Or: pyOperator = PythonOperationKind.BitwiseOr; break;
  60. case ExpressionType.Power: pyOperator = PythonOperationKind.Power; break;
  61. case ExpressionType.RightShift: pyOperator = PythonOperationKind.RightShift; break;
  62. case ExpressionType.LeftShift: pyOperator = PythonOperationKind.LeftShift; break;
  63. case ExpressionType.Subtract: pyOperator = PythonOperationKind.Subtract; break;
  64. case ExpressionType.AddAssign: pyOperator = PythonOperationKind.InPlaceAdd; break;
  65. case ExpressionType.AndAssign: pyOperator = PythonOperationKind.InPlaceBitwiseAnd; break;
  66. case ExpressionType.DivideAssign: pyOperator = PythonOperationKind.InPlaceDivide; break;
  67. case ExpressionType.ExclusiveOrAssign: pyOperator = PythonOperationKind.InPlaceExclusiveOr; break;
  68. case ExpressionType.ModuloAssign: pyOperator = PythonOperationKind.InPlaceMod; break;
  69. case ExpressionType.MultiplyAssign: pyOperator = PythonOperationKind.InPlaceMultiply; break;
  70. case ExpressionType.OrAssign: pyOperator = PythonOperationKind.InPlaceBitwiseOr; break;
  71. case ExpressionType.PowerAssign: pyOperator = PythonOperationKind.InPlacePower; break;
  72. case ExpressionType.RightShiftAssign: pyOperator = PythonOperationKind.InPlaceRightShift; break;
  73. case ExpressionType.LeftShiftAssign: pyOperator = PythonOperationKind.InPlaceLeftShift; break;
  74. case ExpressionType.SubtractAssign: pyOperator = PythonOperationKind.InPlaceSubtract; break;
  75. case ExpressionType.Equal: pyOperator = PythonOperationKind.Equal; break;
  76. case ExpressionType.GreaterThan: pyOperator = PythonOperationKind.GreaterThan; break;
  77. case ExpressionType.GreaterThanOrEqual: pyOperator = PythonOperationKind.GreaterThanOrEqual; break;
  78. case ExpressionType.LessThan: pyOperator = PythonOperationKind.LessThan; break;
  79. case ExpressionType.LessThanOrEqual: pyOperator = PythonOperationKind.LessThanOrEqual; break;
  80. case ExpressionType.NotEqual: pyOperator = PythonOperationKind.NotEqual; break;
  81. }
  82. DynamicMetaObject res = null;
  83. if (pyOperator != null) {
  84. res = MakeBinaryOperation(operation, args, pyOperator.Value, errorSuggestion);
  85. } else {
  86. res = operation.FallbackBinaryOperation(target, arg);
  87. }
  88. return BindingHelpers.AddDynamicTestAndDefer(operation, BindingHelpers.AddPythonBoxing(res), args, valInfo);
  89. }
  90. public static DynamicMetaObject/*!*/ Operation(UnaryOperationBinder/*!*/ operation, DynamicMetaObject arg, DynamicMetaObject errorSuggestion) {
  91. PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Fallback UnaryOperator " + " " + operation.Operation + " " + arg.LimitType.FullName);
  92. PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, operation.Operation.ToString());
  93. DynamicMetaObject[] args = new[] { arg };
  94. if (arg.NeedsDeferral()) {
  95. return operation.Defer(arg);
  96. }
  97. ValidationInfo valInfo = BindingHelpers.GetValidationInfo(args);
  98. DynamicMetaObject res = null;
  99. Type retType = typeof(object);
  100. switch (operation.Operation) {
  101. case ExpressionType.UnaryPlus:
  102. res = BindingHelpers.AddPythonBoxing(MakeUnaryOperation(operation, arg, "__pos__", errorSuggestion));
  103. break;
  104. case ExpressionType.Negate:
  105. res = BindingHelpers.AddPythonBoxing(MakeUnaryOperation(operation, arg, "__neg__", errorSuggestion));
  106. break;
  107. case ExpressionType.OnesComplement:
  108. res = BindingHelpers.AddPythonBoxing(MakeUnaryOperation(operation, arg, "__invert__", errorSuggestion));
  109. break;
  110. case ExpressionType.Not:
  111. res = MakeUnaryNotOperation(operation, arg, typeof(object), errorSuggestion);
  112. break;
  113. case ExpressionType.IsFalse:
  114. res = MakeUnaryNotOperation(operation, arg, typeof(bool), errorSuggestion);
  115. retType = typeof(bool);
  116. break;
  117. case ExpressionType.IsTrue:
  118. res = PythonProtocol.ConvertToBool(operation, arg);
  119. retType = typeof(bool);
  120. break;
  121. default:
  122. res = TypeError(operation, "unknown operation: " + operation.ToString(), args);
  123. break;
  124. }
  125. return BindingHelpers.AddDynamicTestAndDefer(operation, res, args, valInfo, retType);
  126. }
  127. public static DynamicMetaObject/*!*/ Index(DynamicMetaObjectBinder/*!*/ operation, PythonIndexType index, DynamicMetaObject[] args) {
  128. return Index(operation, index, args, null);
  129. }
  130. public static DynamicMetaObject/*!*/ Index(DynamicMetaObjectBinder/*!*/ operation, PythonIndexType index, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion) {
  131. if (args.Length >= 3) {
  132. PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Fallback Index " + " " + index + " " + args[0].LimitType + ", " + args[1].LimitType + ", " + args[2].LimitType + args.Length);
  133. } else {
  134. PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Fallback Index " + " " + index + " " + args[0].LimitType + ", " + args[1].LimitType + args.Length);
  135. }
  136. PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, index.ToString());
  137. if (BindingHelpers.NeedsDeferral(args)) {
  138. return operation.Defer(args);
  139. }
  140. ValidationInfo valInfo = BindingHelpers.GetValidationInfo(args[0]);
  141. DynamicMetaObject res = BindingHelpers.AddPythonBoxing(MakeIndexerOperation(operation, index, args, errorSuggestion));
  142. return BindingHelpers.AddDynamicTestAndDefer(operation, res, args, valInfo);
  143. }
  144. public static DynamicMetaObject/*!*/ Operation(PythonOperationBinder/*!*/ operation, params DynamicMetaObject/*!*/[]/*!*/ args) {
  145. if (args.Length == 1) {
  146. PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Fallback PythonOp " + " " + operation.Operation + " " + args[0].LimitType);
  147. } else {
  148. PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Fallback PythonOp " + " " + operation.Operation + " " + args[0].LimitType + ", " + args[1].LimitType);
  149. }
  150. PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, operation.Operation.ToString());
  151. if (BindingHelpers.NeedsDeferral(args)) {
  152. return operation.Defer(args);
  153. }
  154. return MakeOperationRule(operation, args);
  155. }
  156. private static DynamicMetaObject/*!*/ MakeOperationRule(PythonOperationBinder/*!*/ operation, DynamicMetaObject/*!*/[]/*!*/ args) {
  157. ValidationInfo valInfo = BindingHelpers.GetValidationInfo(args);
  158. DynamicMetaObject res;
  159. Type deferType = typeof(object);
  160. switch (NormalizeOperator(operation.Operation)) {
  161. case PythonOperationKind.Documentation:
  162. res = BindingHelpers.AddPythonBoxing(MakeDocumentationOperation(operation, args));
  163. break;
  164. case PythonOperationKind.CallSignatures:
  165. res = BindingHelpers.AddPythonBoxing(MakeCallSignatureOperation(args[0], CompilerHelpers.GetMethodTargets(args[0].Value)));
  166. break;
  167. case PythonOperationKind.IsCallable:
  168. res = MakeIscallableOperation(operation, args);
  169. break;
  170. case PythonOperationKind.Hash:
  171. res = MakeHashOperation(operation, args[0]);
  172. break;
  173. case PythonOperationKind.Contains:
  174. res = MakeContainsOperation(operation, args);
  175. break;
  176. case PythonOperationKind.AbsoluteValue:
  177. res = BindingHelpers.AddPythonBoxing(MakeUnaryOperation(operation, args[0], "__abs__", null));
  178. break;
  179. case PythonOperationKind.Compare:
  180. res = MakeSortComparisonRule(args, operation, operation.Operation);
  181. Debug.Assert(res.LimitType == typeof(int));
  182. break;
  183. case PythonOperationKind.GetEnumeratorForIteration:
  184. res = MakeEnumeratorOperation(operation, args[0]);
  185. break;
  186. default:
  187. res = BindingHelpers.AddPythonBoxing(MakeBinaryOperation(operation, args, operation.Operation, null));
  188. break;
  189. }
  190. return BindingHelpers.AddDynamicTestAndDefer(operation, res, args, valInfo, deferType);
  191. }
  192. private static DynamicMetaObject MakeBinaryOperation(DynamicMetaObjectBinder operation, DynamicMetaObject/*!*/[] args, PythonOperationKind opStr, DynamicMetaObject errorSuggestion) {
  193. if (IsComparison(opStr)) {
  194. return MakeComparisonOperation(args, operation, opStr, errorSuggestion);
  195. }
  196. return MakeSimpleOperation(args, operation, opStr, errorSuggestion);
  197. }
  198. #region Unary Operations
  199. /// <summary>
  200. /// Creates a rule for the contains operator. This is exposed via "x in y" in
  201. /// IronPython. It is implemented by calling the __contains__ method on x and
  202. /// passing in y.
  203. ///
  204. /// If a type doesn't define __contains__ but does define __getitem__ then __getitem__ is
  205. /// called repeatedly in order to see if the object is there.
  206. ///
  207. /// For normal .NET enumerables we'll walk the iterator and see if it's present.
  208. /// </summary>
  209. private static DynamicMetaObject/*!*/ MakeContainsOperation(PythonOperationBinder/*!*/ operation, DynamicMetaObject/*!*/[]/*!*/ types) {
  210. DynamicMetaObject res;
  211. // the paramteres come in backwards from how we look up __contains__, flip them.
  212. Debug.Assert(types.Length == 2);
  213. ArrayUtils.SwapLastTwo(types);
  214. PythonContext state = PythonContext.GetPythonContext(operation);
  215. SlotOrFunction sf = SlotOrFunction.GetSlotOrFunction(state, "__contains__", types);
  216. if (sf.Success) {
  217. // just a call to __contains__
  218. res = sf.Target;
  219. } else {
  220. RestrictTypes(types);
  221. sf = SlotOrFunction.GetSlotOrFunction(state, "__iter__", types[0]);
  222. if (sf.Success) {
  223. // iterate using __iter__
  224. res = new DynamicMetaObject(
  225. Ast.Call(
  226. typeof(PythonOps).GetMethod("ContainsFromEnumerable"),
  227. AstUtils.Constant(state.SharedContext),
  228. sf.Target.Expression,
  229. AstUtils.Convert(types[1].Expression, typeof(object))
  230. ),
  231. BindingRestrictions.Combine(types)
  232. );
  233. } else {
  234. ParameterExpression curIndex = Ast.Variable(typeof(int), "count");
  235. sf = SlotOrFunction.GetSlotOrFunction(state, "__getitem__", types[0], new DynamicMetaObject(curIndex, BindingRestrictions.Empty));
  236. if (sf.Success) {
  237. // defines __getitem__, need to loop over the indexes and see if we match
  238. ParameterExpression getItemRes = Ast.Variable(sf.ReturnType, "getItemRes");
  239. ParameterExpression containsRes = Ast.Variable(typeof(bool), "containsRes");
  240. LabelTarget target = Ast.Label();
  241. res = new DynamicMetaObject(
  242. Ast.Block(
  243. new ParameterExpression[] { curIndex, getItemRes, containsRes },
  244. Utils.Loop(
  245. null, // test
  246. Ast.Assign(curIndex, Ast.Add(curIndex, AstUtils.Constant(1))), // increment
  247. Ast.Block( // body
  248. // getItemRes = param0.__getitem__(curIndex)
  249. Utils.Try(
  250. Ast.Block(
  251. Ast.Assign(
  252. getItemRes,
  253. sf.Target.Expression
  254. ),
  255. Ast.Empty()
  256. )
  257. ).Catch(
  258. // end of indexes, return false
  259. typeof(IndexOutOfRangeException),
  260. Ast.Break(target)
  261. ),
  262. // if(getItemRes == param1) return true
  263. Utils.If(
  264. DynamicExpression.Dynamic(
  265. state.BinaryOperationRetType(
  266. state.BinaryOperation(ExpressionType.Equal),
  267. state.Convert(typeof(bool), ConversionResultKind.ExplicitCast)
  268. ),
  269. typeof(bool),
  270. types[1].Expression,
  271. getItemRes
  272. ),
  273. Ast.Assign(containsRes, AstUtils.Constant(true)),
  274. Ast.Break(target)
  275. ),
  276. AstUtils.Empty()
  277. ),
  278. null, // loop else
  279. target, // break label target
  280. null
  281. ),
  282. containsRes
  283. ),
  284. BindingRestrictions.Combine(types)
  285. );
  286. } else {
  287. // non-iterable object
  288. res = new DynamicMetaObject(
  289. operation.Throw(
  290. Ast.Call(
  291. typeof(PythonOps).GetMethod("TypeErrorForNonIterableObject"),
  292. AstUtils.Convert(
  293. types[0].Expression,
  294. typeof(object)
  295. )
  296. ),
  297. typeof(bool)
  298. ),
  299. BindingRestrictions.Combine(types)
  300. );
  301. }
  302. }
  303. }
  304. if (res.GetLimitType() != typeof(bool) && res.GetLimitType() != typeof(void)) {
  305. res = new DynamicMetaObject(
  306. DynamicExpression.Dynamic(
  307. state.Convert(
  308. typeof(bool),
  309. ConversionResultKind.ExplicitCast
  310. ),
  311. typeof(bool),
  312. res.Expression
  313. ),
  314. res.Restrictions
  315. );
  316. }
  317. return res;
  318. }
  319. private static void RestrictTypes(DynamicMetaObject/*!*/[] types) {
  320. for (int i = 0; i < types.Length; i++) {
  321. types[i] = types[i].Restrict(types[i].GetLimitType());
  322. }
  323. }
  324. private static DynamicMetaObject/*!*/ MakeHashOperation(PythonOperationBinder/*!*/ operation, DynamicMetaObject/*!*/ self) {
  325. self = self.Restrict(self.GetLimitType());
  326. PythonContext state = PythonContext.GetPythonContext(operation);
  327. SlotOrFunction func = SlotOrFunction.GetSlotOrFunction(state, "__hash__", self);
  328. DynamicMetaObject res = func.Target;
  329. if (func.IsNull) {
  330. // Python 2.6 setting __hash__ = None makes the type unhashable
  331. res = new DynamicMetaObject(
  332. operation.Throw(
  333. Expression.Call(
  334. typeof(PythonOps).GetMethod("TypeErrorForUnhashableObject"),
  335. self.Expression
  336. ),
  337. typeof(int)
  338. ),
  339. res.Restrictions
  340. );
  341. } else if (func.ReturnType != typeof(int)) {
  342. if (func.ReturnType == typeof(BigInteger)) {
  343. // Python 2.5 defines the result of returning a long as hashing the long
  344. res = new DynamicMetaObject(
  345. HashBigInt(operation, res.Expression),
  346. res.Restrictions
  347. );
  348. } else if (func.ReturnType == typeof(object)) {
  349. // need to get the integer value here...
  350. ParameterExpression tempVar = Ast.Parameter(typeof(object), "hashTemp");
  351. res = new DynamicMetaObject(
  352. Expression.Block(
  353. new[] { tempVar },
  354. Expression.Assign(tempVar, res.Expression),
  355. Expression.Condition(
  356. Expression.TypeIs(tempVar, typeof(int)),
  357. Expression.Convert(tempVar, typeof(int)),
  358. Expression.Condition(
  359. Expression.TypeIs(tempVar, typeof(BigInteger)),
  360. HashBigInt(operation, tempVar),
  361. HashConvertToInt(state, tempVar)
  362. )
  363. )
  364. ),
  365. res.Restrictions
  366. );
  367. } else {
  368. // need to convert unknown value to object
  369. res = new DynamicMetaObject(
  370. HashConvertToInt(state, res.Expression),
  371. res.Restrictions
  372. );
  373. }
  374. }
  375. return res;
  376. }
  377. private static DynamicExpression/*!*/ HashBigInt(PythonOperationBinder/*!*/ operation, Expression/*!*/ expression) {
  378. return DynamicExpression.Dynamic(
  379. operation,
  380. typeof(int),
  381. expression
  382. );
  383. }
  384. private static DynamicExpression/*!*/ HashConvertToInt(PythonContext/*!*/ state, Expression/*!*/ expression) {
  385. return DynamicExpression.Dynamic(
  386. state.Convert(
  387. typeof(int),
  388. ConversionResultKind.ExplicitCast
  389. ),
  390. typeof(int),
  391. expression
  392. );
  393. }
  394. private static DynamicMetaObject MakeUnaryOperation(DynamicMetaObjectBinder binder, DynamicMetaObject self, string symbol, DynamicMetaObject errorSuggestion) {
  395. self = self.Restrict(self.GetLimitType());
  396. SlotOrFunction func = SlotOrFunction.GetSlotOrFunction(PythonContext.GetPythonContext(binder), symbol, self);
  397. if (!func.Success) {
  398. // we get the error message w/ {0} so that PythonBinderHelper.TypeError formats it correctly
  399. return errorSuggestion ?? TypeError(binder, MakeUnaryOpErrorMessage(symbol, "{0}"), self);
  400. }
  401. return func.Target;
  402. }
  403. private static DynamicMetaObject MakeEnumeratorOperation(PythonOperationBinder operation, DynamicMetaObject self) {
  404. if (self.GetLimitType() == typeof(string)) {
  405. self = self.Restrict(self.GetLimitType());
  406. return new DynamicMetaObject(
  407. Expression.Call(
  408. typeof(PythonOps).GetMethod("StringEnumerator"),
  409. self.Expression
  410. ),
  411. self.Restrictions
  412. );
  413. } else if (self.GetLimitType() == typeof(Bytes)) {
  414. self = self.Restrict(self.GetLimitType());
  415. if (operation.Context.PythonOptions.Python30) {
  416. return new DynamicMetaObject(
  417. Expression.Call(
  418. typeof(PythonOps).GetMethod("BytesIntEnumerator"),
  419. self.Expression
  420. ),
  421. self.Restrictions
  422. );
  423. } else {
  424. return new DynamicMetaObject(
  425. Expression.Call(
  426. typeof(PythonOps).GetMethod("BytesEnumerator"),
  427. self.Expression
  428. ),
  429. self.Restrictions
  430. );
  431. }
  432. } else if ((self.Value is IEnumerable ||
  433. typeof(IEnumerable).IsAssignableFrom(self.GetLimitType())) && !(self.Value is PythonGenerator)) {
  434. self = self.Restrict(self.GetLimitType());
  435. return new DynamicMetaObject(
  436. Expression.Call(
  437. typeof(PythonOps).GetMethod("GetEnumeratorFromEnumerable"),
  438. Expression.Convert(
  439. self.Expression,
  440. typeof(IEnumerable)
  441. )
  442. ),
  443. self.Restrictions
  444. );
  445. } else if (self.Value is IEnumerator || // check for COM object (and fast check when we have values)
  446. typeof(IEnumerator).IsAssignableFrom(self.GetLimitType())) { // check if we don't have a value
  447. DynamicMetaObject ieres = new DynamicMetaObject(
  448. MakeEnumeratorResult(
  449. Ast.Convert(
  450. self.Expression,
  451. typeof(IEnumerator)
  452. )
  453. ),
  454. self.Restrict(self.GetLimitType()).Restrictions
  455. );
  456. #if FEATURE_COM
  457. if (Microsoft.Scripting.ComInterop.ComBinder.IsComObject(self.Value)) {
  458. ieres = new DynamicMetaObject(
  459. MakeEnumeratorResult(
  460. Expression.Convert(
  461. self.Expression,
  462. typeof(IEnumerator)
  463. )
  464. ),
  465. ieres.Restrictions.Merge(
  466. BindingRestrictions.GetExpressionRestriction(
  467. Ast.TypeIs(self.Expression, typeof(IEnumerator))
  468. )
  469. )
  470. );
  471. }
  472. #endif
  473. return ieres;
  474. }
  475. ParameterExpression tmp = Ast.Parameter(typeof(IEnumerator), "enum");
  476. IPythonConvertible pyConv = self as IPythonConvertible;
  477. PythonConversionBinder convBinder = PythonContext.GetPythonContext(operation).Convert(typeof(IEnumerator), ConversionResultKind.ExplicitTry);
  478. DynamicMetaObject res;
  479. if (pyConv != null) {
  480. res = pyConv.BindConvert(convBinder);
  481. } else {
  482. res = convBinder.Bind(self, new DynamicMetaObject[0]);
  483. }
  484. return new DynamicMetaObject(
  485. Expression.Block(
  486. new[] { tmp },
  487. Ast.Condition(
  488. Ast.NotEqual(
  489. Ast.Assign(tmp, res.Expression),
  490. AstUtils.Constant(null)
  491. ),
  492. MakeEnumeratorResult(tmp),
  493. Ast.Call(
  494. typeof(PythonOps).GetMethod("ThrowTypeErrorForBadIteration"),
  495. PythonContext.GetCodeContext(operation),
  496. self.Expression
  497. )
  498. )
  499. ),
  500. res.Restrictions
  501. );
  502. }
  503. private static NewExpression MakeEnumeratorResult(Expression tmp) {
  504. return Expression.New(
  505. typeof(KeyValuePair<IEnumerator, IDisposable>).GetConstructor(new[] { typeof(IEnumerator), typeof(IDisposable) }),
  506. tmp,
  507. Expression.Constant(null, typeof(IDisposable))
  508. );
  509. }
  510. private static DynamicMetaObject/*!*/ MakeUnaryNotOperation(DynamicMetaObjectBinder/*!*/ operation, DynamicMetaObject/*!*/ self, Type retType, DynamicMetaObject errorSuggestion) {
  511. self = self.Restrict(self.GetLimitType());
  512. SlotOrFunction nonzero = SlotOrFunction.GetSlotOrFunction(PythonContext.GetPythonContext(operation), "__nonzero__", self);
  513. SlotOrFunction length = SlotOrFunction.GetSlotOrFunction(PythonContext.GetPythonContext(operation), "__len__", self);
  514. Expression notExpr;
  515. if (!nonzero.Success && !length.Success) {
  516. // no __len__ or __nonzero__, for None this is always false, everything else is True. If we have
  517. // an error suggestion though we'll go with that.
  518. if (errorSuggestion == null) {
  519. notExpr = (self.GetLimitType() == typeof(DynamicNull)) ? AstUtils.Constant(true) : AstUtils.Constant(false);
  520. } else {
  521. notExpr = errorSuggestion.Expression;
  522. }
  523. } else {
  524. SlotOrFunction target = nonzero.Success ? nonzero : length;
  525. notExpr = target.Target.Expression;
  526. if (nonzero.Success) {
  527. // call non-zero and negate it
  528. if (notExpr.Type == typeof(bool)) {
  529. notExpr = Ast.Equal(notExpr, AstUtils.Constant(false));
  530. } else {
  531. notExpr = Ast.Call(
  532. typeof(PythonOps).GetMethod("Not"),
  533. AstUtils.Convert(notExpr, typeof(object))
  534. );
  535. }
  536. } else {
  537. // call len, compare w/ zero
  538. if (notExpr.Type == typeof(int)) {
  539. notExpr = Ast.Equal(notExpr, AstUtils.Constant(0));
  540. } else {
  541. notExpr =
  542. Ast.Equal(
  543. DynamicExpression.Dynamic(
  544. PythonContext.GetPythonContext(operation).Operation(
  545. PythonOperationKind.Compare
  546. ),
  547. typeof(int),
  548. notExpr,
  549. AstUtils.Constant(0)
  550. ),
  551. AstUtils.Constant(0)
  552. );
  553. }
  554. }
  555. }
  556. if (retType == typeof(object) && notExpr.Type == typeof(bool)) {
  557. notExpr = BindingHelpers.AddPythonBoxing(notExpr);
  558. }
  559. return new DynamicMetaObject(
  560. notExpr,
  561. self.Restrictions.Merge(nonzero.Target.Restrictions.Merge(length.Target.Restrictions))
  562. );
  563. }
  564. #endregion
  565. #region Reflective Operations
  566. private static DynamicMetaObject/*!*/ MakeDocumentationOperation(PythonOperationBinder/*!*/ operation, DynamicMetaObject/*!*/[]/*!*/ args) {
  567. PythonContext state = PythonContext.GetPythonContext(operation);
  568. return new DynamicMetaObject(
  569. Binders.Convert(
  570. PythonContext.GetCodeContext(operation),
  571. state,
  572. typeof(string),
  573. ConversionResultKind.ExplicitCast,
  574. Binders.Get(
  575. PythonContext.GetCodeContext(operation),
  576. state,
  577. typeof(object),
  578. "__doc__",
  579. args[0].Expression
  580. )
  581. ),
  582. args[0].Restrictions
  583. );
  584. }
  585. internal static DynamicMetaObject/*!*/ MakeCallSignatureOperation(DynamicMetaObject/*!*/ self, IList<MethodBase/*!*/>/*!*/ targets) {
  586. List<string> arrres = new List<string>();
  587. foreach (MethodBase mb in targets) {
  588. StringBuilder res = new StringBuilder();
  589. string comma = "";
  590. Type retType = mb.GetReturnType();
  591. if (retType != typeof(void)) {
  592. res.Append(DynamicHelpers.GetPythonTypeFromType(retType).Name);
  593. res.Append(" ");
  594. }
  595. MethodInfo mi = mb as MethodInfo;
  596. if (mi != null) {
  597. string name;
  598. NameConverter.TryGetName(DynamicHelpers.GetPythonTypeFromType(mb.DeclaringType), mi, out name);
  599. res.Append(name);
  600. } else {
  601. res.Append(DynamicHelpers.GetPythonTypeFromType(mb.DeclaringType).Name);
  602. }
  603. res.Append("(");
  604. if (!CompilerHelpers.IsStatic(mb)) {
  605. res.Append("self");
  606. comma = ", ";
  607. }
  608. foreach (ParameterInfo pi in mb.GetParameters()) {
  609. if (pi.ParameterType == typeof(CodeContext)) continue;
  610. res.Append(comma);
  611. res.Append(DynamicHelpers.GetPythonTypeFromType(pi.ParameterType).Name + " " + pi.Name);
  612. comma = ", ";
  613. }
  614. res.Append(")");
  615. arrres.Add(res.ToString());
  616. }
  617. return new DynamicMetaObject(
  618. AstUtils.Constant(arrres.ToArray()),
  619. self.Restrictions.Merge(BindingRestrictions.GetInstanceRestriction(self.Expression, self.Value))
  620. );
  621. }
  622. private static DynamicMetaObject/*!*/ MakeIscallableOperation(PythonOperationBinder/*!*/ operation, DynamicMetaObject/*!*/[]/*!*/ args) {
  623. // Certain non-python types (encountered during interop) are callable, but don't have
  624. // a __call__ attribute. The default base binder also checks these, but since we're overriding
  625. // the base binder, we check them here.
  626. DynamicMetaObject self = args[0];
  627. // only applies when called from a Python site
  628. if (typeof(Delegate).IsAssignableFrom(self.GetLimitType()) ||
  629. typeof(MethodGroup).IsAssignableFrom(self.GetLimitType())) {
  630. return new DynamicMetaObject(
  631. AstUtils.Constant(true),
  632. self.Restrict(self.GetLimitType()).Restrictions
  633. );
  634. }
  635. PythonContext state = PythonContext.GetPythonContext(operation);
  636. Expression isCallable = Ast.NotEqual(
  637. Binders.TryGet(
  638. PythonContext.GetCodeContext(operation),
  639. state,
  640. typeof(object),
  641. "__call__",
  642. self.Expression
  643. ),
  644. AstUtils.Constant(OperationFailed.Value)
  645. );
  646. return new DynamicMetaObject(
  647. isCallable,
  648. self.Restrict(self.GetLimitType()).Restrictions
  649. );
  650. }
  651. #endregion
  652. #region Common Binary Operations
  653. private static DynamicMetaObject/*!*/ MakeSimpleOperation(DynamicMetaObject/*!*/[]/*!*/ types, DynamicMetaObjectBinder/*!*/ binder, PythonOperationKind operation, DynamicMetaObject errorSuggestion) {
  654. RestrictTypes(types);
  655. SlotOrFunction fbinder;
  656. SlotOrFunction rbinder;
  657. PythonTypeSlot fSlot;
  658. PythonTypeSlot rSlot;
  659. GetOperatorMethods(types, operation, PythonContext.GetPythonContext(binder), out fbinder, out rbinder, out fSlot, out rSlot);
  660. return MakeBinaryOperatorResult(types, binder, operation, fbinder, rbinder, fSlot, rSlot, errorSuggestion);
  661. }
  662. private static void GetOperatorMethods(DynamicMetaObject/*!*/[]/*!*/ types, PythonOperationKind oper, PythonContext state, out SlotOrFunction fbinder, out SlotOrFunction rbinder, out PythonTypeSlot fSlot, out PythonTypeSlot rSlot) {
  663. oper = NormalizeOperator(oper);
  664. oper &= ~PythonOperationKind.InPlace;
  665. string op, rop;
  666. if (!IsReverseOperator(oper)) {
  667. op = Symbols.OperatorToSymbol(oper);
  668. rop = Symbols.OperatorToReversedSymbol(oper);
  669. } else {
  670. // coming back after coercion, just try reverse operator.
  671. rop = Symbols.OperatorToSymbol(oper);
  672. op = Symbols.OperatorToReversedSymbol(oper);
  673. }
  674. fSlot = null;
  675. rSlot = null;
  676. PythonType fParent, rParent;
  677. if (oper == PythonOperationKind.Multiply &&
  678. IsSequence(types[0]) &&
  679. !PythonOps.IsNonExtensibleNumericType(types[1].GetLimitType())) {
  680. // class M:
  681. // def __rmul__(self, other):
  682. // print "CALLED"
  683. // return 1
  684. //
  685. // print [1,2] * M()
  686. //
  687. // in CPython this results in a successful call to __rmul__ on the type ignoring the forward
  688. // multiplication. But calling the __mul__ method directly does NOT return NotImplemented like
  689. // one might expect. Therefore we explicitly convert the MetaObject argument into an Index
  690. // for binding purposes. That allows this to work at multiplication time but not with
  691. // a direct call to __mul__.
  692. DynamicMetaObject[] newTypes = new DynamicMetaObject[2];
  693. newTypes[0] = types[0];
  694. newTypes[1] = new DynamicMetaObject(
  695. Ast.New(
  696. typeof(Index).GetConstructor(new Type[] { typeof(object) }),
  697. AstUtils.Convert(types[1].Expression, typeof(object))
  698. ),
  699. BindingRestrictions.Empty
  700. );
  701. types = newTypes;
  702. }
  703. if (!SlotOrFunction.TryGetBinder(state, types, op, null, out fbinder, out fParent)) {
  704. foreach (PythonType pt in MetaPythonObject.GetPythonType(types[0]).ResolutionOrder) {
  705. if (pt.TryLookupSlot(state.SharedContext, op, out fSlot)) {
  706. fParent = pt;
  707. break;
  708. }
  709. }
  710. }
  711. if (!SlotOrFunction.TryGetBinder(state, types, null, rop, out rbinder, out rParent)) {
  712. foreach (PythonType pt in MetaPythonObject.GetPythonType(types[1]).ResolutionOrder) {
  713. if (pt.TryLookupSlot(state.SharedContext, rop, out rSlot)) {
  714. rParent = pt;
  715. break;
  716. }
  717. }
  718. }
  719. if (fParent != null && (rbinder.Success || rSlot != null) && rParent != fParent && rParent.IsSubclassOf(fParent)) {
  720. // Python says if x + subx and subx defines __r*__ we should call r*.
  721. fbinder = SlotOrFunction.Empty;
  722. fSlot = null;
  723. }
  724. if (!fbinder.Success && !rbinder.Success && fSlot == null && rSlot == null) {
  725. if (op == "__truediv__" || op == "__rtruediv__") {
  726. // true div on a type which doesn't support it, go ahead and try normal divide
  727. PythonOperationKind newOp = op == "__truediv__" ? PythonOperationKind.Divide : PythonOperationKind.ReverseDivide;
  728. GetOperatorMethods(types, newOp, state, out fbinder, out rbinder, out fSlot, out rSlot);
  729. }
  730. }
  731. }
  732. private static bool IsReverseOperator(PythonOperationKind oper) {
  733. return (oper & PythonOperationKind.Reversed) != 0;
  734. }
  735. private static bool IsSequence(DynamicMetaObject/*!*/ metaObject) {
  736. if (typeof(List).IsAssignableFrom(metaObject.GetLimitType()) ||
  737. typeof(PythonTuple).IsAssignableFrom(metaObject.GetLimitType()) ||
  738. typeof(String).IsAssignableFrom(metaObject.GetLimitType())) {
  739. return true;
  740. }
  741. return false;
  742. }
  743. private static DynamicMetaObject/*!*/ MakeBinaryOperatorResult(DynamicMetaObject/*!*/[]/*!*/ types, DynamicMetaObjectBinder/*!*/ operation, PythonOperationKind op, SlotOrFunction/*!*/ fCand, SlotOrFunction/*!*/ rCand, PythonTypeSlot fSlot, PythonTypeSlot rSlot, DynamicMetaObject errorSuggestion) {
  744. Assert.NotNull(operation, fCand, rCand);
  745. SlotOrFunction fTarget, rTarget;
  746. PythonContext state = PythonContext.GetPythonContext(operation);
  747. ConditionalBuilder bodyBuilder = new ConditionalBuilder(operation);
  748. if ((op & PythonOperationKind.InPlace) != 0) {
  749. // in place operator, see if there's a specific method that handles it.
  750. SlotOrFunction function = SlotOrFunction.GetSlotOrFunction(PythonContext.GetPythonContext(operation), Symbols.OperatorToSymbol(op), types);
  751. // we don't do a coerce for in place operators if the lhs implements __iop__
  752. if (!MakeOneCompareGeneric(function, false, types, MakeCompareReturn, bodyBuilder, typeof(object))) {
  753. // the method handles it and always returns a useful value.
  754. return bodyBuilder.GetMetaObject(types);
  755. }
  756. }
  757. if (!SlotOrFunction.GetCombinedTargets(fCand, rCand, out fTarget, out rTarget) &&
  758. fSlot == null &&
  759. rSlot == null &&
  760. !ShouldCoerce(state, op, types[0], types[1], false) &&
  761. !ShouldCoerce(state, op, types[1], types[0], false) &&
  762. bodyBuilder.NoConditions) {
  763. return MakeRuleForNoMatch(operation, op, errorSuggestion, types);
  764. }
  765. if (ShouldCoerce(state, op, types[0], types[1], false) &&
  766. (op != PythonOperationKind.Mod || !MetaPythonObject.GetPythonType(types[0]).IsSubclassOf(TypeCache.String))) {
  767. // need to try __coerce__ first.
  768. DoCoerce(state, bodyBuilder, op, types, false);
  769. }
  770. if (MakeOneTarget(PythonContext.GetPythonContext(operation), fTarget, fSlot, bodyBuilder, false, types)) {
  771. if (ShouldCoerce(state, op, types[1], types[0], false)) {
  772. // need to try __coerce__ on the reverse first
  773. DoCoerce(state, bodyBuilder, op, new DynamicMetaObject[] { types[1], types[0] }, true);
  774. }
  775. if (rSlot != null) {
  776. MakeSlotCall(PythonContext.GetPythonContext(operation), types, bodyBuilder, rSlot, true);
  777. bodyBuilder.FinishCondition(MakeBinaryThrow(operation, op, types).Expression, typeof(object));
  778. } else if (MakeOneTarget(PythonContext.GetPythonContext(operation), rTarget, rSlot, bodyBuilder, false, types)) {
  779. // need to fallback to throwing or coercion
  780. bodyBuilder.FinishCondition(MakeBinaryThrow(operation, op, types).Expression, typeof(object));
  781. }
  782. }
  783. return bodyBuilder.GetMetaObject(types);
  784. }
  785. private static void MakeCompareReturn(ConditionalBuilder/*!*/ bodyBuilder, Expression retCondition, Expression/*!*/ retValue, bool isReverse, Type retType) {
  786. if (retCondition != null) {
  787. bodyBuilder.AddCondition(retCondition, retValue);
  788. } else {
  789. bodyBuilder.FinishCondition(retValue, retType);
  790. }
  791. }
  792. /// <summary>
  793. /// Delegate for finishing the comparison. This takes in a condition and a return value and needs to update the ConditionalBuilder
  794. /// with the appropriate resulting body. The condition may be null.
  795. /// </summary>
  796. private delegate void ComparisonHelper(ConditionalBuilder/*!*/ bodyBuilder, Expression retCondition, Expression/*!*/ retValue, bool isReverse, Type retType);
  797. /// <summary>
  798. /// Helper to handle a comparison operator call. Checks to see if the call can
  799. /// return NotImplemented and allows the caller to modify the expression that
  800. /// is ultimately returned (e.g. to turn __cmp__ into a bool after a comparison)
  801. /// </summary>
  802. private static bool MakeOneCompareGeneric(SlotOrFunction/*!*/ target, bool reverse, DynamicMetaObject/*!*/[]/*!*/ types, ComparisonHelper returner, ConditionalBuilder/*!*/ bodyBuilder, Type retType) {
  803. if (target == SlotOrFunction.Empty || !target.Success) return true;
  804. ParameterExpression tmp;
  805. if (target.ReturnType == typeof(bool)) {
  806. tmp = bodyBuilder.CompareRetBool;
  807. } else {
  808. tmp = Ast.Variable(target.ReturnType, "compareRetValue");
  809. bodyBuilder.AddVariable(tmp);
  810. }
  811. if (target.MaybeNotImplemented) {
  812. Expression call = target.Target.Expression;
  813. Expression assign = Ast.Assign(tmp, call);
  814. returner(
  815. bodyBuilder,
  816. Ast.NotEqual(
  817. assign,
  818. AstUtils.Constant(PythonOps.NotImplemented)
  819. ),
  820. tmp,
  821. reverse,
  822. retType);
  823. return true;
  824. } else {
  825. returner(
  826. bodyBuilder,
  827. null,
  828. target.Target.Expression,
  829. reverse,
  830. retType
  831. );
  832. return false;
  833. }
  834. }
  835. private static bool MakeOneTarget(PythonContext/*!*/ state, SlotOrFunction/*!*/ target, PythonTypeSlot slotTarget, ConditionalBuilder/*!*/ bodyBuilder, bool reverse, DynamicMetaObject/*!*/[]/*!*/ types) {
  836. if (target == SlotOrFunction.Empty && slotTarget == null) return true;
  837. if (slotTarget != null) {
  838. MakeSlotCall(state, types, bodyBuilder, slotTarget, reverse);
  839. return true;
  840. } else if (target.MaybeNotImplemented) {
  841. Debug.Assert(target.ReturnType == typeof(object));
  842. ParameterExpression tmp = Ast.Variable(typeof(object), "slot");
  843. bodyBuilder.AddVariable(tmp);
  844. bodyBuilder.AddCondition(
  845. Ast.NotEqual(
  846. Ast.Assign(
  847. tmp,
  848. target.Target.Expression
  849. ),
  850. Ast.Property(null, typeof(PythonOps).GetProperty("NotImplemented"))
  851. ),
  852. tmp
  853. );
  854. return true;
  855. } else {
  856. bodyBuilder.FinishCondition(target.Target.Expression, typeof(object));
  857. return false;
  858. }
  859. }
  860. private static void MakeSlotCall(PythonContext/*!*/ state, DynamicMetaObject/*!*/[]/*!*/ types, ConditionalBuilder/*!*/ bodyBuilder, PythonTypeSlot/*!*/ slotTarget, bool reverse) {
  861. Debug.Assert(slotTarget != null);
  862. Expression self, other;
  863. if (reverse) {
  864. self = types[1].Expression;
  865. other = types[0].Expression;
  866. } else {
  867. self = types[0].Expression;
  868. other = types[1].Expression;
  869. }
  870. MakeSlotCallWorker(state, slotTarget, self, bodyBuilder, other);
  871. }
  872. private static void MakeSlotCallWorker(PythonContext/*!*/ state, PythonTypeSlot/*!*/ slotTarget, Expression/*!*/ self, ConditionalBuilder/*!*/ bodyBuilder, params Expression/*!*/[]/*!*/ args) {
  873. // Generate:
  874. //
  875. // SlotTryGetValue(context, slot, selfType, out callable) && (tmp=callable(args)) != NotImplemented) ?
  876. // tmp :
  877. // RestOfOperation
  878. //
  879. ParameterExpression callable = Ast.Variable(typeof(object), "slot");
  880. ParameterExpression tmp = Ast.Variable(typeof(object), "slot");
  881. bodyBuilder.AddCondition(
  882. Ast.AndAlso(
  883. Ast.Call(
  884. typeof(PythonOps).GetMethod("SlotTryGetValue"),
  885. AstUtils.Constant(state.SharedContext),
  886. AstUtils.Convert(Utils.WeakConstant(slotTarget), typeof(PythonTypeSlot)),
  887. AstUtils.Convert(self, typeof(object)),
  888. Ast.Call(
  889. typeof(DynamicHelpers).GetMethod("GetPythonType"),
  890. AstUtils.Convert(self, typeof(object))
  891. ),
  892. callable
  893. ),
  894. Ast.NotEqual(
  895. Ast.Assign(
  896. tmp,
  897. DynamicExpression.Dynamic(
  898. state.Invoke(
  899. new CallSignature(args.Length)
  900. ),
  901. typeof(object),
  902. ArrayUtils.Insert(AstUtils.Constant(state.SharedContext), (Expression)callable, args)
  903. )
  904. ),
  905. Ast.Property(null, typeof(PythonOps).GetProperty("NotImplemented"))
  906. )
  907. ),
  908. tmp
  909. );
  910. bodyBuilder.AddVariable(callable);
  911. bodyBuilder.AddVariable(tmp);
  912. }
  913. private static void DoCoerce(PythonContext/*!*/ state, ConditionalBuilder/*!*/ bodyBuilder, PythonOperationKind op, DynamicMetaObject/*!*/[]/*!*/ types, bool reverse) {
  914. DoCoerce(state, bodyBuilder, op, types, reverse, delegate(Expression e) {
  915. return e;
  916. });
  917. }
  918. /// <summary>
  919. /// calls __coerce__ for old-style classes and performs the operation if the coercion is successful.
  920. /// </summary>
  921. private static void DoCoerce(PythonContext/*!*/ pyContext, ConditionalBuilder/*!*/ bodyBuilder, PythonOperationKind op, DynamicMetaObject/*!*/[]/*!*/ types, bool reverse, Func<Expression, Expression> returnTransform) {
  922. ParameterExpression coerceResult = Ast.Variable(typeof(object), "coerceResult");
  923. ParameterExpression coerceTuple = Ast.Variable(typeof(PythonTuple), "coerceTuple");
  924. // tmp = self.__coerce__(other)
  925. // if tmp != null && tmp != NotImplemented && (tuple = PythonOps.ValidateCoerceResult(tmp)) != null:
  926. // return operation(tuple[0], tuple[1])
  927. SlotOrFunction slot = SlotOrFunction.GetSlotOrFunction(pyContext, "__coerce__", types);
  928. if (slot.Success) {
  929. bodyBuilder.AddCondition(
  930. Ast.AndAlso(
  931. Ast.Not(
  932. Ast.TypeIs(
  933. Ast.Assign(
  934. coerceResult,
  935. slot.Target.Expression
  936. ),
  937. typeof(OldInstance)
  938. )
  939. ),
  940. Ast.NotEqual(
  941. Ast.Assign(
  942. coerceTuple,
  943. Ast.Call(
  944. typeof(PythonOps).GetMethod("ValidateCoerceResult"),
  945. coerceResult
  946. )
  947. ),
  948. AstUtils.Constant(null)
  949. )
  950. ),
  951. BindingHelpers.AddRecursionCheck(
  952. pyContext,
  953. returnTransform(
  954. DynamicExpression.Dynamic(
  955. pyContext.Operation(op | PythonOperationKind.DisableCoerce),
  956. op == PythonOperationKind.Compare ? typeof(int) : typeof(object),
  957. reverse ? CoerceTwo(coerceTuple) : CoerceOne(coerceTuple),
  958. reverse ? CoerceOne(coerceTuple) : CoerceTwo(coerceTuple)
  959. )
  960. )
  961. )
  962. );
  963. bodyBuilder.AddVariable(coerceResult);
  964. bodyBuilder.AddVariable(coerceTuple);
  965. }
  966. }
  967. private static MethodCallExpression/*!*/ CoerceTwo(ParameterExpression/*!*/ coerceTuple) {
  968. return Ast.Call(
  969. typeof(PythonOps).GetMethod("GetCoerceResultTwo"),
  970. coerceTuple
  971. );
  972. }
  973. private static MethodCallExpression/*!*/ CoerceOne(ParameterExpression/*!*/ coerceTuple) {
  974. return Ast.Call(
  975. typeof(PythonOps).GetMethod("GetCoerceResultOne"),
  976. coerceTuple
  977. );
  978. }
  979. #endregion
  980. #region Comparison Operations
  981. private static DynamicMetaObject/*!*/ MakeComparisonOperation(DynamicMetaObject/*!*/[]/*!*/ types, DynamicMetaObjectBinder/*!*/ operation, PythonOperationKind opString, DynamicMetaObject errorSuggestion) {
  982. RestrictTypes(types);
  983. PythonOperationKind op = NormalizeOperator(opString);
  984. PythonContext state = PythonContext.GetPythonContext(operation);
  985. Debug.Assert(types.Length == 2);
  986. DynamicMetaObject xType = types[0], yType = types[1];
  987. string opSym = Symbols.OperatorToSymbol(op);
  988. string ropSym = Symbols.OperatorToReversedSymbol(op);
  989. // reverse
  990. DynamicMetaObject[] rTypes = new DynamicMetaObject[] { types[1], types[0] };
  991. SlotOrFunction fop, rop, cmp, rcmp;
  992. fop = SlotOrFunction.GetSlotOrFunction(state, opSym, types);
  993. rop = SlotOrFunction.GetSlotOrFunction(state, ropSym, rTypes);
  994. cmp = SlotOrFunction.GetSlotOrFunction(state, "__cmp__", types);
  995. rcmp = SlotOrFunction.GetSlotOrFunction(state, "__cmp__", rTypes);
  996. ConditionalBuilder bodyBuilder = new ConditionalBuilder(operation);
  997. SlotOrFunction.GetCombinedTargets(fop, rop, out fop, out rop);
  998. SlotOrFunction.GetCombinedTargets(cmp, rcmp, out cmp, out rcmp);
  999. bool shouldWarn = false;
  1000. WarningInfo info = null;
  1001. // first try __op__ or __rop__ and return the value
  1002. shouldWarn = fop.ShouldWarn(state, out info);
  1003. if (MakeOneCompareGeneric(fop, false, types, MakeCompareReturn, bodyBuilder, typeof(object))) {
  1004. shouldWarn = shouldWarn || rop.ShouldWarn(state, out info);
  1005. if (MakeOneCompareGeneric(rop, true, types, MakeCompareReturn, bodyBuilder, typeof(object))) {
  1006. // then try __cmp__ or __rcmp__ and compare the resulting int appropriaetly
  1007. shouldWarn = shouldWarn || cmp.ShouldWarn(state, out info);
  1008. if (ShouldCoerce(state, opString, xType, yType, true)) {
  1009. DoCoerce(state, bodyBuilder, PythonOperationKind.Compare, types, false, delegate(Expression e) {
  1010. return GetCompareTest(op, e, false);
  1011. });
  1012. }
  1013. if (MakeOneCompareGeneric(
  1014. cmp,
  1015. false,
  1016. types,
  1017. delegate(ConditionalBuilder builder, Expression retCond, Expression expr, bool reverse, Type retType) {
  1018. MakeCompareTest(op, builder, retCond, expr, reverse, retType);
  1019. },
  1020. bodyBuilder,
  1021. typeof(object))) {
  1022. shouldWarn = shouldWarn || rcmp.ShouldWarn(state, out info);
  1023. if (ShouldCoerce(state, opString, yType, xType, true)) {
  1024. DoCoerce(state, bodyBuilder, PythonOperationKind.Compare, rTypes, true, delegate(Expression e) {
  1025. return GetCompareTest(op, e, true);
  1026. });
  1027. }
  1028. if (MakeOneCompareGeneric(
  1029. rcmp,
  1030. true,
  1031. types,
  1032. delegate(ConditionalBuilder builder, Expression retCond, Expression expr, bool reverse, Type retType) {
  1033. MakeCompareTest(op, builder, retCond, expr, reverse, retType);
  1034. },
  1035. bodyBuilder,
  1036. typeof(object))) {
  1037. if (errorSuggestion != null) {
  1038. bodyBuilder.FinishCondition(errorSuggestion.Expression, typeof(object));
  1039. } else {
  1040. bodyBuilder.FinishCondition(BindingHelpers.AddPythonBoxing(MakeFallbackCompare(operation, op, types)), typeof(object));
  1041. }
  1042. }
  1043. }
  1044. }
  1045. }
  1046. DynamicMetaObject res = bodyBuilder.GetMetaObject(types);
  1047. if (!shouldWarn || res == null) {
  1048. return res;
  1049. } else {
  1050. return info.AddWarning(Ast.Constant(state.SharedContext), res);
  1051. }
  1052. }
  1053. /// <summary>
  1054. /// Makes the comparison rule which returns an int (-1, 0, 1). TODO: Better name?
  1055. /// </summary>
  1056. private static DynamicMetaObject/*!*/ MakeSortComparisonRule(DynamicMetaObject/*!*/[]/*!*/ types, DynamicMetaObjectBinder/*!*/ operation, PythonOperationKind op) {
  1057. RestrictTypes(types);
  1058. DynamicMetaObject fastPath = FastPathCompare(types);
  1059. if (fastPath != null) {
  1060. return fastPath;
  1061. }
  1062. // Python compare semantics:
  1063. // if the types are the same invoke __cmp__ first.
  1064. // If __cmp__ is not defined or the types are different:
  1065. // try rich comparisons (eq, lt, gt, etc...)
  1066. // If the types are not the same and rich cmp didn't work finally try __cmp__
  1067. // If __cmp__ isn't defined return a comparison based upon the types.
  1068. //
  1069. // Along the way we try both forward and reverse versions (try types[0] and then
  1070. // try types[1] reverse version). For these comparisons __cmp__ and __eq__ are their
  1071. // own reversals and __gt__ is the opposite of __lt__.
  1072. // collect all the comparison methods, most likely we won't need them all.
  1073. DynamicMetaObject[] rTypes = new DynamicMetaObject[] { types[1], types[0] };
  1074. SlotOrFunction cfunc, rcfunc, eqfunc, reqfunc, ltfunc, gtfunc, rltfunc, rgtfunc;
  1075. PythonContext state = PythonContext.GetPythonContext(operation);
  1076. cfunc = SlotOrFunction.GetSlotOrFunction(state, "__cmp__", types);
  1077. rcfunc = SlotOrFunction.GetSlotOrFunction(state, "__cmp__", rTypes);
  1078. eqfunc = SlotOrFunction.GetSlotOrFunction(state, "__eq__", types);
  1079. reqfunc = SlotOrFunction.GetSlotOrFunction(state, "__eq__", rTypes);
  1080. ltfunc = SlotOrFunction.GetSlotOrFunction(state, "__lt__", types);
  1081. gtfunc = SlotOrFunction.GetSlotOrFunction(state, "__gt__", types);
  1082. rltfunc = SlotOrFunction.GetSlotOrFunction(state, "__lt__", rTypes);
  1083. rgtfunc = SlotOrFunction.GetSlotOrFunction(state, "__gt__", rTypes);
  1084. // inspect forward and reverse versions so we can pick one or both.
  1085. SlotOrFunction cTarget, rcTarget, eqTarget, reqTarget, ltTarget, rgtTarget, gtTarget, rltTarget;
  1086. SlotOrFunction.GetCombinedTargets(cfunc, rcfunc, out cTarget, out rcTarget);
  1087. SlotOrFunction.GetCombinedTargets(eqfunc, reqfunc, out eqTarget, out reqTarget);
  1088. SlotOrFunction.GetCombinedTargets(ltfunc, rgtfunc, out ltTarget, out rgtTarget);
  1089. SlotOrFunction.GetCombinedTargets(gtfunc, rltfunc, out gtTarget, out rltTarget);
  1090. PythonType xType = MetaPythonObject.GetPythonType(types[0]);
  1091. PythonType yType = MetaPythonObject.GetPythonType(types[1]);
  1092. // now build the rule from the targets.
  1093. // bail if we're comparing to null and the rhs can't do anything special...
  1094. if (xType.IsNull) {
  1095. if (yType.IsNull) {
  1096. return new DynamicMetaObject(
  1097. AstUtils.Constant(0),
  1098. BindingRestrictions.Combine(types)
  1099. );
  1100. } else if (yType.UnderlyingSystemType.IsPrimitive() || yType.UnderlyingSystemType == typeof(BigInteger)) {
  1101. return new DynamicMetaObject(
  1102. AstUtils.Constant(-1),
  1103. BindingRestrictions.Combine(types)
  1104. );
  1105. }
  1106. }
  1107. ConditionalBuilder bodyBuilder = new ConditionalBuilder(operation);
  1108. bool tryRich = true, more = true;
  1109. if (xType == yType && cTarget != SlotOrFunction.Empty) {
  1110. // if the types are equal try __cmp__ first
  1111. if (ShouldCoerce(state, op, types[0], types[1], true)) {
  1112. // need to try __coerce__ first.
  1113. DoCoerce(state, bodyBuilder, PythonOperationKind.Compare, types, false);
  1114. }
  1115. more = more && MakeOneCompareGeneric(cTarget, false, types, MakeCompareReverse, bodyBuilder, typeof(int));
  1116. if (xType != TypeCache.OldInstance) {
  1117. // try __cmp__ backwards for new-style classes and don't fallback to
  1118. // rich comparisons if available
  1119. more = more && MakeOneCompareGeneric(rcTarget, true, types, MakeCompareReverse, bodyBuilder, typeof(int));
  1120. tryRich = false;
  1121. }
  1122. }
  1123. if (tryRich && more) {
  1124. // try the >, <, ==, !=, >=, <=. These don't get short circuited using the more logic
  1125. // because they don't give a definitive answer even if they return bool. Only if they
  1126. // return true do we know to return 0, -1, or 1.
  1127. // try eq
  1128. MakeOneCompareGeneric(eqTarget, false, types, MakeCompareToZero, bodyBuilder, typeof(int));
  1129. MakeOneCompareGeneric(reqTarget, true, types, MakeCompareToZero, bodyBuilder, typeof(int));
  1130. // try less than & reverse
  1131. MakeOneCompareGeneric(ltTarget, false, types, MakeCompareToNegativeOne, bodyBuilder, typeof(int));
  1132. MakeOneCompareGeneric(rgtTarget, true, types, MakeCompareToNegativeOne, bodyBuilder, typeof(int));
  1133. // try greater than & reverse
  1134. MakeOneCompareGeneric(gtTarget, false, types, MakeCompareToOne, bodyBuilder, typeof(int));
  1135. MakeOneCompareGeneric(rltTarget, true, types, MakeCompareToOne, bodyBuilder, typeof(int));
  1136. }
  1137. if (xType != yType) {
  1138. if (more && ShouldCoerce(state, op, types[0], types[1], true)) {
  1139. // need to try __coerce__ first.
  1140. DoCoerce(state, bodyBuilder, PythonOperationKind.Compare, types, false);
  1141. }
  1142. more = more && MakeOneCompareGeneric(cTarget, false, types, MakeCompareReverse, bodyBuilder, typeof(int));
  1143. if (more && ShouldCoerce(state, op, types[1], types[0], true)) {
  1144. // try __coerce__ first
  1145. DoCoerce(state, bodyBuilder, PythonOperationKind.Compare, rTypes, true, delegate(Expression e) {
  1146. return ReverseCompareValue(e);
  1147. });
  1148. }
  1149. more = more && MakeOneCompareGeneric(rcTarget, true, types, MakeCompareReverse, bodyBuilder, typeof(int));
  1150. }
  1151. if (more) {
  1152. // fall back to compare types
  1153. bodyBuilder.FinishCondition(MakeFallbackCompare(operation, op, types), typeof(int));
  1154. }
  1155. return bodyBuilder.GetMetaObject(types);
  1156. }
  1157. private static DynamicMetaObject FastPathCompare(DynamicMetaObject/*!*/[] types) {
  1158. if (types[0].GetLimitType() == types[1].GetLimitType()) {
  1159. // fast paths for comparing some types which don't define __cmp__
  1160. if (types[0].GetLimitType() == typeof(List)) {
  1161. types[0] = types[0].Restrict(typeof(List));
  1162. types[1] = types[1].Restrict(typeof(List));
  1163. return new DynamicMetaObject(
  1164. Ast.Call(
  1165. typeof(PythonOps).GetMethod("CompareLists"),
  1166. types[0].Expression,
  1167. types[1].Expression
  1168. ),
  1169. BindingRestrictions.Combine(types)
  1170. );
  1171. } else if (types[0].GetLimitType() == typeof(PythonTuple)) {
  1172. types[0] = types[0].Restrict(typeof(PythonTuple));
  1173. types[1] = types[1].Restrict(typeof(PythonTuple));
  1174. return new DynamicMetaObject(
  1175. Ast.Call(
  1176. typeof(PythonOps).GetMethod("CompareTuples"),
  1177. types[0].Expression,
  1178. types[1].Expression
  1179. ),
  1180. BindingRestrictions.Combine(types)
  1181. );
  1182. } else if (types[0].GetLimitType() == typeof(double)) {
  1183. types[0] = types[0].Restrict(typeof(double));
  1184. types[1] = types[1].Restrict(typeof(double));
  1185. return new DynamicMetaObject(
  1186. Ast.Call(
  1187. typeof(PythonOps).GetMethod("CompareFloats"),
  1188. types[0].Expression,
  1189. types[1].Expression
  1190. ),
  1191. BindingRestrictions.Combine(types)
  1192. );
  1193. }
  1194. }
  1195. return null;
  1196. }
  1197. private static void MakeCompareToZero(ConditionalBuilder/*!*/ bodyBuilder, Expression retCondition, Expression/*!*/ expr, bool reverse, Type retType) {
  1198. MakeValueCheck(0, expr, bodyBuilder, retCondition);
  1199. }
  1200. private static void MakeCompareToOne(ConditionalBuilder/*!*/ bodyBuilder, Expression retCondition, Expression/*!*/ expr, bool reverse, Type retType) {
  1201. MakeValueCheck(1, expr, bodyBuilder, retCondition);
  1202. }
  1203. private static void MakeCompareToNegativeOne(ConditionalBuilder/*!*/ bodyBuilder, Expression retCondition, Expression/*!*/ expr, bool reverse, Type retType) {
  1204. MakeValueCheck(-1, expr, bodyBuilder, retCondition);
  1205. }
  1206. private static void MakeValueCheck(int val, Expression retValue, ConditionalBuilder/*!*/ bodyBuilder, Expression retCondition) {
  1207. if (retValue.Type != typeof(bool)) {
  1208. retValue = DynamicExpression.Dynamic(
  1209. PythonContext.GetPythonContext(bodyBuilder.Action).Convert(
  1210. typeof(bool),
  1211. ConversionResultKind.ExplicitCast
  1212. ),
  1213. typeof(bool),
  1214. retValue
  1215. );
  1216. }
  1217. if (retCondition != null) {
  1218. retValue = Ast.AndAlso(retCondition, retValue);
  1219. }
  1220. bodyBuilder.AddCondition(
  1221. retValue,
  1222. AstUtils.Constant(val)
  1223. );
  1224. }
  1225. private static BinaryExpression/*!*/ ReverseCompareValue(Expression/*!*/ retVal) {
  1226. return Ast.Multiply(
  1227. AstUtils.Convert(
  1228. retVal,
  1229. typeof(int)
  1230. ),
  1231. AstUtils.Constant(-1)
  1232. );
  1233. }
  1234. private static void MakeCompareReverse(ConditionalBuilder/*!*/ bodyBuilder, Expression retCondition, Expression/*!*/ expr, bool reverse, Type retType) {
  1235. Expression res = expr;
  1236. if (reverse) {
  1237. res = ReverseCompareValue(expr);
  1238. }
  1239. MakeCompareReturn(bodyBuilder, retCondition, res, reverse, retType);
  1240. }
  1241. private static void MakeCompareTest(PythonOperationKind op, ConditionalBuilder/*!*/ bodyBuilder, Expression retCond, Expression/*!*/ expr, bool reverse, Type retType) {
  1242. MakeCompareReturn(bodyBuilder, retCond, GetCompareTest(op, expr, reverse), reverse, retType);
  1243. }
  1244. private static Expression/*!*/ MakeFallbackCompare(DynamicMetaObjectBinder/*!*/ binder, PythonOperationKind op, DynamicMetaObject[] types) {
  1245. return Ast.Call(
  1246. GetComparisonFallbackMethod(op),
  1247. PythonContext.GetCodeContext(binder),
  1248. AstUtils.Convert(types[0].Expression, typeof(object)),
  1249. AstUtils.Convert(types[1].Expression, typeof(object))
  1250. );
  1251. }
  1252. private static Expression GetCompareTest(PythonOperationKind op, Expression expr, bool reverse) {
  1253. if (expr.Type == typeof(int)) {
  1254. // fast path, just do a compare in IL
  1255. return GetCompareNode(op, reverse, expr);
  1256. } else {
  1257. return GetCompareExpression(
  1258. op,
  1259. reverse,
  1260. Ast.Call(
  1261. typeof(PythonOps).GetMethod("CompareToZero"),
  1262. AstUtils.Convert(expr, typeof(object))
  1263. )
  1264. );
  1265. }
  1266. }
  1267. #endregion
  1268. #region Index Operations
  1269. /// <summary>
  1270. /// Python has three protocols for slicing:
  1271. /// Simple Slicing x[i:j]
  1272. /// Extended slicing x[i,j,k,...]
  1273. /// Long Slice x[start:stop:step]
  1274. ///
  1275. /// The first maps to __*slice__ (get, set, and del).
  1276. /// This takes indexes - i, j - which specify the range of elements to be
  1277. /// returned. In the slice variants both i, j must be numeric data types.
  1278. /// The 2nd and 3rd are both __*item__.
  1279. /// This receives a single index which is either a Tuple or a Slice object (which
  1280. /// encapsulates the start, stop, and step values)
  1281. ///
  1282. /// This is in addition to a simple indexing x[y].
  1283. ///
  1284. /// For simple slicing and long slicing Python generates Operators.*Slice. For
  1285. /// the extended slicing and simple indexing Python generates a Operators.*Item
  1286. /// action.
  1287. ///
  1288. /// Extended slicing maps to the normal .NET multi-parameter input.
  1289. ///
  1290. /// So our job here is to first determine if we're to call a __*slice__ method or
  1291. /// a __*item__ method.
  1292. /// </summary>
  1293. private static DynamicMetaObject/*!*/ MakeIndexerOperation(DynamicMetaObjectBinder/*!*/ operation, PythonIndexType op, DynamicMetaObject/*!*/[]/*!*/ types, DynamicMetaObject errorSuggestion) {
  1294. string item, slice;
  1295. DynamicMetaObject indexedType = types[0].Restrict(types[0].GetLimitType());
  1296. PythonContext state = PythonContext.GetPythonContext(operation);
  1297. BuiltinFunction itemFunc = null;
  1298. PythonTypeSlot itemSlot = null;
  1299. bool callSlice = false;
  1300. int mandatoryArgs;
  1301. GetIndexOperators(op, out item, out slice, out mandatoryArgs);
  1302. if (types.Length == mandatoryArgs + 1 && IsSlice(op) && HasOnlyNumericTypes(operation, types, op == PythonIndexType.SetSlice)) {
  1303. // two slice indexes, all int arguments, need to call __*slice__ if it exists
  1304. callSlice = BindingHelpers.TryGetStaticFunction(state, slice, indexedType, out itemFunc);
  1305. if (itemFunc == null || !callSlice) {
  1306. callSlice = MetaPythonObject.GetPythonType(indexedType).TryResolveSlot(state.SharedContext, slice, out itemSlot);
  1307. }
  1308. }
  1309. if (!callSlice) {
  1310. // 1 slice index (simple index) or multiple slice indexes or no __*slice__, call __*item__,
  1311. if (!BindingHelpers.TryGetStaticFunction(state, item, indexedType, out itemFunc)) {
  1312. MetaPythonObject.GetPythonType(indexedType).TryResolveSlot(state.SharedContext, item, out itemSlot);
  1313. }
  1314. }
  1315. // make the Callable object which does the actual call to the function or slot
  1316. Callable callable = Callable.MakeCallable(state, op, itemFunc, itemSlot);
  1317. if (callable == null) {
  1318. return errorSuggestion ?? MakeUnindexableError(operation, op, types, indexedType, state);
  1319. }
  1320. // prepare the arguments and make the builder which will
  1321. // call __*slice__ or __*item__
  1322. DynamicMetaObject[] args;
  1323. IndexBuilder builder;
  1324. if (callSlice) {
  1325. // we're going to call a __*slice__ method, we pass the args as is.
  1326. Debug.Assert(IsSlice(op));
  1327. builder = new SliceBuilder(types, callable);
  1328. // slicing is dependent upon the types of the arguments (HasNumericTypes)
  1329. // so we must restrict them.
  1330. args = ConvertArgs(types);
  1331. } else {
  1332. // we're going to call a __*item__ method.
  1333. builder = new ItemBuilder(types, callable);
  1334. if (IsSlice(op)) {
  1335. // we need to create a new Slice object.
  1336. args = GetItemSliceArguments(state, op, types);
  1337. } else {
  1338. // no need to restrict the arguments. We're not
  1339. // a slice and so restrictions are not necessary
  1340. // here because it's not dependent upon our types.
  1341. args = (DynamicMetaObject[])types.Clone();
  1342. // but we do need to restrict based upon the type
  1343. // of object we're calling on.
  1344. args[0] = types[0].Restrict(types[0].GetLimitType());
  1345. }
  1346. }
  1347. return builder.MakeRule(operation, state, args);
  1348. }
  1349. private static DynamicMetaObject MakeUnindexableError(DynamicMetaObjectBinder operation, PythonIndexType op, DynamicMetaObject/*!*/[] types, DynamicMetaObject indexedType, PythonContext state) {
  1350. DynamicMetaObject[] newTypes = (DynamicMetaObject[])types.Clone();
  1351. newTypes[0] = indexedType;
  1352. PythonTypeSlot dummySlot;
  1353. if (op != PythonIndexType.GetItem &&
  1354. op != PythonIndexType.GetSlice &&
  1355. DynamicHelpers.GetPythonType(indexedType.Value).TryResolveSlot(state.SharedContext, "__getitem__", out dummySlot)) {
  1356. // object supports indexing but not setting/deletion
  1357. if (op == PythonIndexType.SetItem || op == PythonIndexType.SetSlice) {
  1358. return TypeError(operation, "'{0}' object does not support item assignment", newTypes);
  1359. } else {
  1360. return TypeError(operation, "'{0}' object doesn't support item deletion", newTypes);
  1361. }
  1362. }
  1363. return TypeError(operation, "'{0}' object is not subscriptable", newTypes);
  1364. }
  1365. /// <summary>
  1366. /// Helper to convert all of the arguments to their known types.
  1367. /// </summary>
  1368. private static DynamicMetaObject/*!*/[]/*!*/ ConvertArgs(DynamicMetaObject/*!*/[]/*!*/ types) {
  1369. DynamicMetaObject[] res = new DynamicMetaObject[types.Length];
  1370. for (int i = 0; i < types.Length; i++) {
  1371. res[i] = types[i].Restrict(types[i].GetLimitType());
  1372. }
  1373. return res;
  1374. }
  1375. /// <summary>
  1376. /// Gets the arguments that need to be provided to __*item__ when we need to pass a slice object.
  1377. /// </summary>
  1378. private static DynamicMetaObject/*!*/[]/*!*/ GetItemSliceArguments(PythonContext state, PythonIndexType op, DynamicMetaObject/*!*/[]/*!*/ types) {
  1379. DynamicMetaObject[] args;
  1380. if (op == PythonIndexType.SetSlice) {
  1381. args = new DynamicMetaObject[] {
  1382. types[0].Restrict(types[0].GetLimitType()),
  1383. GetSetSlice(state, types),
  1384. types[types.Length- 1].Restrict(types[types.Length - 1].GetLimitType())
  1385. };
  1386. } else {
  1387. Debug.Assert(op == PythonIndexType.GetSlice || op == PythonIndexType.DeleteSlice);
  1388. args = new DynamicMetaObject[] {
  1389. types[0].Restrict(types[0].GetLimitType()),
  1390. GetGetOrDeleteSlice(state, types)
  1391. };
  1392. }
  1393. return args;
  1394. }
  1395. /// <summary>
  1396. /// Base class for calling indexers. We have two subclasses that target built-in functions and user defined callable objects.
  1397. ///
  1398. /// The Callable objects get handed off to ItemBuilder's which then call them with the appropriate arguments.
  1399. /// </summary>
  1400. abstract class Callable {
  1401. private readonly PythonContext/*!*/ _binder;
  1402. private readonly PythonIndexType _op;
  1403. protected Callable(PythonContext/*!*/ binder, PythonIndexType op) {
  1404. Assert.NotNull(binder);
  1405. _binder = binder;
  1406. _op = op;
  1407. }
  1408. /// <summary>
  1409. /// Creates a new CallableObject. If BuiltinFunction is available we'll create a BuiltinCallable otherwise
  1410. /// we create a SlotCallable.
  1411. /// </summary>
  1412. public static Callable MakeCallable(PythonContext/*!*/ binder, PythonIndexType op, BuiltinFunction itemFunc, PythonTypeSlot itemSlot) {
  1413. if (itemFunc != null) {
  1414. // we'll call a builtin function to produce the rule
  1415. return new BuiltinCallable(binder, op, itemFunc);
  1416. } else if (itemSlot != null) {
  1417. // we'll call a PythonTypeSlot to produce the rule
  1418. return new SlotCallable(binder, op, itemSlot);
  1419. }
  1420. return null;
  1421. }
  1422. /// <summary>
  1423. /// Gets the arguments in a form that should be used for extended slicing.
  1424. ///
  1425. /// Python defines that multiple tuple arguments received (x[1,2,3]) get
  1426. /// packed into a Tuple. For most .NET methods we just want to expand
  1427. /// this into the multiple index arguments. For slots and old-instances
  1428. /// we want to pass in the tuple
  1429. /// </summary>
  1430. public virtual DynamicMetaObject[] GetTupleArguments(DynamicMetaObject[] arguments) {
  1431. if (IsSetter) {
  1432. if (arguments.Length == 3) {
  1433. // simple setter, no extended slicing, no need to pack arguments into tuple
  1434. return arguments;
  1435. }
  1436. // we want self, (tuple, of, args, ...), value
  1437. Expression[] tupleArgs = new Expression[arguments.Length - 2];
  1438. BindingRestrictions restrictions = BindingRestrictions.Empty;
  1439. for (int i = 1; i < arguments.Length - 1; i++) {
  1440. tupleArgs[i - 1] = AstUtils.Convert(arguments[i].Expression, typeof(object));
  1441. restrictions = restrictions.Merge(arguments[i].Restrictions);
  1442. }
  1443. return new DynamicMetaObject[] {
  1444. arguments[0],
  1445. new DynamicMetaObject(
  1446. Ast.Call(
  1447. typeof(PythonOps).GetMethod("MakeTuple"),
  1448. Ast.NewArrayInit(typeof(object), tupleArgs)
  1449. ),
  1450. restrictions
  1451. ),
  1452. arguments[arguments.Length-1]
  1453. };
  1454. } else if (arguments.Length == 2) {
  1455. // simple getter, no extended slicing, no need to pack arguments into tuple
  1456. return arguments;
  1457. } else {
  1458. // we want self, (tuple, of, args, ...)
  1459. Expression[] tupleArgs = new Expression[arguments.Length - 1];
  1460. for (int i = 1; i < arguments.Length; i++) {
  1461. tupleArgs[i - 1] = AstUtils.Convert(arguments[i].Expression, typeof(object));
  1462. }
  1463. return new DynamicMetaObject[] {
  1464. arguments[0],
  1465. new DynamicMetaObject(
  1466. Ast.Call(
  1467. typeof(PythonOps).GetMethod("MakeTuple"),
  1468. Ast.NewArrayInit(typeof(object), tupleArgs)
  1469. ),
  1470. BindingRestrictions.Combine(ArrayUtils.RemoveFirst(arguments))
  1471. )
  1472. };
  1473. }
  1474. }
  1475. /// <summary>
  1476. /// Adds the target of the call to the rule.
  1477. /// </summary>
  1478. public abstract DynamicMetaObject/*!*/ CompleteRuleTarget(DynamicMetaObjectBinder/*!*/ metaBinder, DynamicMetaObject[] args, Func<DynamicMetaObject> customFailure);
  1479. protected PythonBinder Binder {
  1480. get { return _binder.Binder; }
  1481. }
  1482. protected PythonContext PythonContext {
  1483. get { return _binder; }
  1484. }
  1485. protected bool IsSetter {
  1486. get { return _op == PythonIndexType.SetItem || _op == PythonIndexType.SetSlice; }
  1487. }
  1488. }
  1489. /// <summary>
  1490. /// Subclass of Callable for a built-in function. This calls a .NET method performing
  1491. /// the appropriate bindings.
  1492. /// </summary>
  1493. class BuiltinCallable : Callable {
  1494. private readonly BuiltinFunction/*!*/ _bf;
  1495. public BuiltinCallable(PythonContext/*!*/ binder, PythonIndexType op, BuiltinFunction/*!*/ func)
  1496. : base(binder, op) {
  1497. Assert.NotNull(func);
  1498. _bf = func;
  1499. }
  1500. public override DynamicMetaObject[] GetTupleArguments(DynamicMetaObject[] arguments) {
  1501. if (arguments[0].GetLimitType() == typeof(OldInstance)) {
  1502. // old instances are special in that they take only a single parameter
  1503. // in their indexer but accept multiple parameters as tuples.
  1504. return base.GetTupleArguments(arguments);
  1505. }
  1506. return arguments;
  1507. }
  1508. public override DynamicMetaObject/*!*/ CompleteRuleTarget(DynamicMetaObjectBinder/*!*/ metaBinder, DynamicMetaObject/*!*/[]/*!*/ args, Func<DynamicMetaObject> customFailure) {
  1509. Assert.NotNull(args);
  1510. Assert.NotNullItems(args);
  1511. BindingTarget target;
  1512. var resolver = new PythonOverloadResolver(
  1513. Binder,
  1514. args[0],
  1515. ArrayUtils.RemoveFirst(args),
  1516. new CallSignature(args.Length - 1),
  1517. AstUtils.Constant(PythonContext.SharedContext)
  1518. );
  1519. DynamicMetaObject res = Binder.CallMethod(
  1520. resolver,
  1521. _bf.Targets,
  1522. BindingRestrictions.Combine(args),
  1523. _bf.Name,
  1524. PythonNarrowing.None,
  1525. PythonNarrowing.IndexOperator,
  1526. out target
  1527. );
  1528. res = BindingHelpers.CheckLightThrowMO(metaBinder, res, target);
  1529. if (target.Success) {
  1530. if (IsSetter) {
  1531. res = new DynamicMetaObject(
  1532. Ast.Block(res.Expression, args[args.Length - 1].Expression),
  1533. res.Restrictions
  1534. );
  1535. }
  1536. WarningInfo info;
  1537. if (BindingWarnings.ShouldWarn(Binder.Context, target.Overload, out info)) {
  1538. res = info.AddWarning(Ast.Constant(PythonContext.SharedContext), res);
  1539. }
  1540. } else if (customFailure == null || (res = customFailure()) == null) {
  1541. res = DefaultBinder.MakeError(resolver.MakeInvalidParametersError(target), BindingRestrictions.Combine(ConvertArgs(args)), typeof(object));
  1542. }
  1543. return res;
  1544. }
  1545. }
  1546. /// <summary>
  1547. /// Callable to a user-defined callable object. This could be a Python function,
  1548. /// a class defining __call__, etc...
  1549. /// </summary>
  1550. class SlotCallable : Callable {
  1551. private PythonTypeSlot _slot;
  1552. public SlotCallable(PythonContext/*!*/ binder, PythonIndexType op, PythonTypeSlot slot)
  1553. : base(binder, op) {
  1554. _slot = slot;
  1555. }
  1556. public override DynamicMetaObject/*!*/ CompleteRuleTarget(DynamicMetaObjectBinder/*!*/ metaBinder, DynamicMetaObject/*!*/[]/*!*/ args, Func<DynamicMetaObject> customFailure) {
  1557. ConditionalBuilder cb = new ConditionalBuilder();
  1558. _slot.MakeGetExpression(
  1559. Binder,
  1560. AstUtils.Constant(PythonContext.SharedContext),
  1561. args[0],
  1562. new DynamicMetaObject(
  1563. Ast.Call(
  1564. typeof(DynamicHelpers).GetMethod("GetPythonType"),
  1565. AstUtils.Convert(args[0].Expression, typeof(object))
  1566. ),
  1567. BindingRestrictions.Empty,
  1568. DynamicHelpers.GetPythonType(args[0].Value)
  1569. ),
  1570. cb
  1571. );
  1572. if (!cb.IsFinal) {
  1573. cb.FinishCondition(metaBinder.Throw(Ast.New(typeof(InvalidOperationException))));
  1574. }
  1575. Expression callable = cb.GetMetaObject().Expression;
  1576. Expression[] exprArgs = new Expression[args.Length - 1];
  1577. for (int i = 1; i < args.Length; i++) {
  1578. exprArgs[i - 1] = args[i].Expression;
  1579. }
  1580. Expression retVal = DynamicExpression.Dynamic(
  1581. PythonContext.Invoke(
  1582. new CallSignature(exprArgs.Length)
  1583. ),
  1584. typeof(object),
  1585. ArrayUtils.Insert(AstUtils.Constant(PythonContext.SharedContext), (Expression)callable, exprArgs)
  1586. );
  1587. if (IsSetter) {
  1588. retVal = Ast.Block(retVal, args[args.Length - 1].Expression);
  1589. }
  1590. return new DynamicMetaObject(
  1591. retVal,
  1592. BindingRestrictions.Combine(args)
  1593. );
  1594. }
  1595. }
  1596. /// <summary>
  1597. /// Base class for building a __*item__ or __*slice__ call.
  1598. /// </summary>
  1599. abstract class IndexBuilder {
  1600. private readonly Callable/*!*/ _callable;
  1601. private readonly DynamicMetaObject/*!*/[]/*!*/ _types;
  1602. public IndexBuilder(DynamicMetaObject/*!*/[]/*!*/ types, Callable/*!*/ callable) {
  1603. _callable = callable;
  1604. _types = types;
  1605. }
  1606. public abstract DynamicMetaObject/*!*/ MakeRule(DynamicMetaObjectBinder/*!*/ metaBinder, PythonContext/*!*/ binder, DynamicMetaObject/*!*/[]/*!*/ args);
  1607. protected Callable/*!*/ Callable {
  1608. get { return _callable; }
  1609. }
  1610. protected PythonType/*!*/ GetTypeAt(int index) {
  1611. return MetaPythonObject.GetPythonType(_types[index]);
  1612. }
  1613. }
  1614. /// <summary>
  1615. /// Derived IndexBuilder for calling __*slice__ methods
  1616. /// </summary>
  1617. class SliceBuilder : IndexBuilder {
  1618. private ParameterExpression _lengthVar; // Nullable<int>, assigned if we need to calculate the length of the object during the call.
  1619. public SliceBuilder(DynamicMetaObject/*!*/[]/*!*/ types, Callable/*!*/ callable)
  1620. : base(types, callable) {
  1621. }
  1622. public override DynamicMetaObject/*!*/ MakeRule(DynamicMetaObjectBinder/*!*/ metaBinder, PythonContext/*!*/ binder, DynamicMetaObject/*!*/[]/*!*/ args) {
  1623. // the semantics of simple slicing state that if the value
  1624. // is less than 0 then the length is added to it. The default
  1625. // for unprovided parameters are 0 and maxint. The callee
  1626. // is responsible for ignoring out of range values but slicing
  1627. // is responsible for doing this initial transformation.
  1628. Debug.Assert(args.Length > 2); // index 1 and 2 should be our slice indexes, we might have another arg if we're a setter
  1629. args = ArrayUtils.Copy(args);
  1630. for (int i = 1; i < 3; i++) {
  1631. args[i] = args[i].Restrict(args[i].GetLimitType());
  1632. if (args[i].GetLimitType() == typeof(MissingParameter)) {
  1633. switch (i) {
  1634. case 1: args[i] = new DynamicMetaObject(AstUtils.Constant(0), args[i].Restrictions); break;
  1635. case 2: args[i] = new DynamicMetaObject(AstUtils.Constant(Int32.MaxValue), args[i].Restrictions); break;
  1636. }
  1637. } else if (args[i].GetLimitType() == typeof(int)) {
  1638. args[i] = MakeIntTest(args[0], args[i]);
  1639. } else if (args[i].GetLimitType().IsSubclassOf(typeof(Extensible<int>))) {
  1640. args[i] = MakeIntTest(
  1641. args[0],
  1642. new DynamicMetaObject(
  1643. Ast.Property(
  1644. args[i].Expression,
  1645. args[i].GetLimitType().GetProperty("Value")
  1646. ),
  1647. args[i].Restrictions
  1648. )
  1649. );
  1650. } else if (args[i].GetLimitType() == typeof(BigInteger)) {
  1651. args[i] = MakeBigIntTest(args[0], args[i]);
  1652. } else if (args[i].GetLimitType().IsSubclassOf(typeof(Extensible<BigInteger>))) {
  1653. args[i] = MakeBigIntTest(args[0], new DynamicMetaObject(Ast.Property(args[i].Expression, args[i].GetLimitType().GetProperty("Value")), args[i].Restrictions));
  1654. } else if (args[i].GetLimitType() == typeof(bool)) {
  1655. args[i] = new DynamicMetaObject(
  1656. Ast.Condition(args[i].Expression, AstUtils.Constant(1), AstUtils.Constant(0)),
  1657. args[i].Restrictions
  1658. );
  1659. } else {
  1660. // this type defines __index__, otherwise we'd have an ItemBuilder constructing a slice
  1661. args[i] = MakeIntTest(args[0],
  1662. new DynamicMetaObject(
  1663. DynamicExpression.Dynamic(
  1664. binder.Convert(
  1665. typeof(int),
  1666. ConversionResultKind.ExplicitCast
  1667. ),
  1668. typeof(int),
  1669. DynamicExpression.Dynamic(
  1670. binder.InvokeNone,
  1671. typeof(object),
  1672. AstUtils.Constant(binder.SharedContext),
  1673. Binders.Get(
  1674. AstUtils.Constant(binder.SharedContext),
  1675. binder,
  1676. typeof(object),
  1677. "__index__",
  1678. Ast.Convert(
  1679. args[i].Expression,
  1680. typeof(object)
  1681. )
  1682. )
  1683. )
  1684. ),
  1685. args[i].Restrictions
  1686. )
  1687. );
  1688. }
  1689. }
  1690. if (_lengthVar != null) {
  1691. // we need the length which we should only calculate once, calculate and
  1692. // store it in a temporary. Note we only calculate the length if we'll
  1693. DynamicMetaObject res = Callable.CompleteRuleTarget(metaBinder, args, null);
  1694. return new DynamicMetaObject(
  1695. Ast.Block(
  1696. new ParameterExpression[] { _lengthVar },
  1697. Ast.Assign(_lengthVar, AstUtils.Constant(null, _lengthVar.Type)),
  1698. res.Expression
  1699. ),
  1700. res.Restrictions
  1701. );
  1702. }
  1703. return Callable.CompleteRuleTarget(metaBinder, args, null);
  1704. }
  1705. private DynamicMetaObject/*!*/ MakeBigIntTest(DynamicMetaObject/*!*/ self, DynamicMetaObject/*!*/ bigInt) {
  1706. EnsureLengthVariable();
  1707. return new DynamicMetaObject(
  1708. Ast.Call(
  1709. typeof(PythonOps).GetMethod("NormalizeBigInteger"),
  1710. self.Expression,
  1711. bigInt.Expression,
  1712. _lengthVar
  1713. ),
  1714. self.Restrictions.Merge(bigInt.Restrictions)
  1715. );
  1716. }
  1717. private DynamicMetaObject/*!*/ MakeIntTest(DynamicMetaObject/*!*/ self, DynamicMetaObject/*!*/ intVal) {
  1718. return new DynamicMetaObject(
  1719. Ast.Condition(
  1720. Ast.LessThan(intVal.Expression, AstUtils.Constant(0)),
  1721. Ast.Add(intVal.Expression, MakeGetLength(self)),
  1722. intVal.Expression
  1723. ),
  1724. self.Restrictions.Merge(intVal.Restrictions)
  1725. );
  1726. }
  1727. private Expression/*!*/ MakeGetLength(DynamicMetaObject /*!*/ self) {
  1728. EnsureLengthVariable();
  1729. return Ast.Call(
  1730. typeof(PythonOps).GetMethod("GetLengthOnce"),
  1731. self.Expression,
  1732. _lengthVar
  1733. );
  1734. }
  1735. private void EnsureLengthVariable() {
  1736. if (_lengthVar == null) {
  1737. _lengthVar = Ast.Variable(typeof(Nullable<int>), "objLength");
  1738. }
  1739. }
  1740. }
  1741. /// <summary>
  1742. /// Derived IndexBuilder for calling __*item__ methods.
  1743. /// </summary>
  1744. class ItemBuilder : IndexBuilder {
  1745. public ItemBuilder(DynamicMetaObject/*!*/[]/*!*/ types, Callable/*!*/ callable)
  1746. : base(types, callable) {
  1747. }
  1748. public override DynamicMetaObject/*!*/ MakeRule(DynamicMetaObjectBinder/*!*/ metaBinder, PythonContext/*!*/ binder, DynamicMetaObject/*!*/[]/*!*/ args) {
  1749. DynamicMetaObject[] tupleArgs = Callable.GetTupleArguments(args);
  1750. return Callable.CompleteRuleTarget(metaBinder, tupleArgs, delegate() {
  1751. PythonTypeSlot indexSlot;
  1752. if (args[1].GetLimitType() != typeof(Slice) && GetTypeAt(1).TryResolveSlot(binder.SharedContext, "__index__", out indexSlot)) {
  1753. args[1] = new DynamicMetaObject(
  1754. DynamicExpression.Dynamic(
  1755. binder.Convert(
  1756. typeof(int),
  1757. ConversionResultKind.ExplicitCast
  1758. ),
  1759. typeof(int),
  1760. DynamicExpression.Dynamic(
  1761. binder.InvokeNone,
  1762. typeof(object),
  1763. AstUtils.Constant(binder.SharedContext),
  1764. Binders.Get(
  1765. AstUtils.Constant(binder.SharedContext),
  1766. binder,
  1767. typeof(object),
  1768. "__index__",
  1769. args[1].Expression
  1770. )
  1771. )
  1772. ),
  1773. BindingRestrictions.Empty
  1774. );
  1775. return Callable.CompleteRuleTarget(metaBinder, tupleArgs, null);
  1776. }
  1777. return null;
  1778. });
  1779. }
  1780. }
  1781. private static bool HasOnlyNumericTypes(DynamicMetaObjectBinder/*!*/ action, DynamicMetaObject/*!*/[]/*!*/ types, bool skipLast) {
  1782. bool onlyNumeric = true;
  1783. PythonContext state = PythonContext.GetPythonContext(action);
  1784. for (int i = 1; i < (skipLast ? types.Length - 1 : types.Length); i++) {
  1785. DynamicMetaObject obj = types[i];
  1786. if (!IsIndexType(state, obj)) {
  1787. onlyNumeric = false;
  1788. break;
  1789. }
  1790. }
  1791. return onlyNumeric;
  1792. }
  1793. private static bool IsIndexType(PythonContext/*!*/ state, DynamicMetaObject/*!*/ obj) {
  1794. bool numeric = true;
  1795. if (obj.GetLimitType() != typeof(MissingParameter) &&
  1796. !PythonOps.IsNumericType(obj.GetLimitType())) {
  1797. PythonType curType = MetaPythonObject.GetPythonType(obj);
  1798. PythonTypeSlot dummy;
  1799. if (!curType.TryResolveSlot(state.SharedContext, "__index__", out dummy)) {
  1800. numeric = false;
  1801. }
  1802. }
  1803. return numeric;
  1804. }
  1805. private static bool IsSlice(PythonIndexType op) {
  1806. return op >= PythonIndexType.GetSlice;
  1807. }
  1808. /// <summary>
  1809. /// Helper to get the symbols for __*item__ and __*slice__ based upon if we're doing
  1810. /// a get/set/delete and the minimum number of arguments required for each of those.
  1811. /// </summary>
  1812. private static void GetIndexOperators(PythonIndexType op, out string item, out string slice, out int mandatoryArgs) {
  1813. switch (op) {
  1814. case PythonIndexType.GetItem:
  1815. case PythonIndexType.GetSlice:
  1816. item = "__getitem__";
  1817. slice = "__getslice__";
  1818. mandatoryArgs = 2;
  1819. return;
  1820. case PythonIndexType.SetItem:
  1821. case PythonIndexType.SetSlice:
  1822. item = "__setitem__";
  1823. slice = "__setslice__";
  1824. mandatoryArgs = 3;
  1825. return;
  1826. case PythonIndexType.DeleteItem:
  1827. case PythonIndexType.DeleteSlice:
  1828. item = "__delitem__";
  1829. slice = "__delslice__";
  1830. mandatoryArgs = 2;
  1831. return;
  1832. }
  1833. throw new InvalidOperationException();
  1834. }
  1835. private static DynamicMetaObject/*!*/ GetSetSlice(PythonContext state, DynamicMetaObject/*!*/[]/*!*/ args) {
  1836. DynamicMetaObject[] newArgs = (DynamicMetaObject[])args.Clone();
  1837. for (int i = 1; i < newArgs.Length; i++) {
  1838. if (!IsIndexType(state, newArgs[i])) {
  1839. newArgs[i] = newArgs[i].Restrict(newArgs[i].GetLimitType());
  1840. }
  1841. }
  1842. return new DynamicMetaObject(
  1843. Ast.Call(
  1844. typeof(PythonOps).GetMethod("MakeSlice"),
  1845. AstUtils.Convert(GetSetParameter(newArgs, 1), typeof(object)),
  1846. AstUtils.Convert(GetSetParameter(newArgs, 2), typeof(object)),
  1847. AstUtils.Convert(GetSetParameter(newArgs, 3), typeof(object))
  1848. ),
  1849. BindingRestrictions.Combine(newArgs)
  1850. );
  1851. }
  1852. private static DynamicMetaObject/*!*/ GetGetOrDeleteSlice(PythonContext state, DynamicMetaObject/*!*/[]/*!*/ args) {
  1853. DynamicMetaObject[] newArgs = (DynamicMetaObject[])args.Clone();
  1854. for (int i = 1; i < newArgs.Length; i++) {
  1855. if (!IsIndexType(state, newArgs[i])) {
  1856. newArgs[i] = newArgs[i].Restrict(newArgs[i].GetLimitType());
  1857. }
  1858. }
  1859. return new DynamicMetaObject(
  1860. Ast.Call(
  1861. typeof(PythonOps).GetMethod("MakeSlice"),
  1862. AstUtils.Convert(GetGetOrDeleteParameter(newArgs, 1), typeof(object)),
  1863. AstUtils.Convert(GetGetOrDeleteParameter(newArgs, 2), typeof(object)),
  1864. AstUtils.Convert(GetGetOrDeleteParameter(newArgs, 3), typeof(object))
  1865. ),
  1866. BindingRestrictions.Combine(newArgs)
  1867. );
  1868. }
  1869. private static Expression/*!*/ GetGetOrDeleteParameter(DynamicMetaObject/*!*/[]/*!*/ args, int index) {
  1870. if (args.Length > index) {
  1871. return CheckMissing(args[index].Expression);
  1872. }
  1873. return AstUtils.Constant(null);
  1874. }
  1875. private static Expression GetSetParameter(DynamicMetaObject[] args, int index) {
  1876. if (args.Length > (index + 1)) {
  1877. return CheckMissing(args[index].Expression);
  1878. }
  1879. return AstUtils.Constant(null);
  1880. }
  1881. #endregion
  1882. #region Helpers
  1883. /// <summary>
  1884. /// Checks if a coercion check should be performed. We perform coercion under the following
  1885. /// situations:
  1886. /// 1. Old instances performing a binary operator (excluding rich comparisons)
  1887. /// 2. User-defined new instances calling __cmp__ but only if we wouldn't dispatch to a built-in __coerce__ on the parent type
  1888. ///
  1889. /// This matches the behavior of CPython.
  1890. /// </summary>
  1891. /// <returns></returns>
  1892. private static bool ShouldCoerce(PythonContext/*!*/ state, PythonOperationKind operation, DynamicMetaObject/*!*/ x, DynamicMetaObject/*!*/ y, bool isCompare) {
  1893. if ((operation & PythonOperationKind.DisableCoerce) != 0) {
  1894. return false;
  1895. }
  1896. PythonType xType = MetaPythonObject.GetPythonType(x), yType = MetaPythonObject.GetPythonType(y);
  1897. if (xType == TypeCache.OldInstance) return true;
  1898. if (isCompare && !xType.IsSystemType && yType.IsSystemType) {
  1899. if (yType == TypeCache.Int32 ||
  1900. yType == TypeCache.BigInteger ||
  1901. yType == TypeCache.Double ||
  1902. yType == TypeCache.Complex) {
  1903. // only coerce new style types that define __coerce__ and
  1904. // only when comparing against built-in types which
  1905. // define __coerce__
  1906. PythonTypeSlot pts;
  1907. if (xType.TryResolveSlot(state.SharedContext, "__coerce__", out pts)) {
  1908. // don't call __coerce__ if it's declared on the base type
  1909. BuiltinMethodDescriptor bmd = pts as BuiltinMethodDescriptor;
  1910. if (bmd == null) return true;
  1911. if (bmd.__name__ != "__coerce__" &&
  1912. bmd.DeclaringType != typeof(int) &&
  1913. bmd.DeclaringType != typeof(BigInteger) &&
  1914. bmd.DeclaringType != typeof(double) &&
  1915. bmd.DeclaringType != typeof(Complex)) {
  1916. return true;
  1917. }
  1918. foreach (PythonType pt in xType.ResolutionOrder) {
  1919. if (pt.UnderlyingSystemType == bmd.DeclaringType) {
  1920. // inherited __coerce__
  1921. return false;
  1922. }
  1923. }
  1924. return true;
  1925. }
  1926. }
  1927. }
  1928. return false;
  1929. }
  1930. public static PythonOperationKind DirectOperation(PythonOperationKind op) {
  1931. if ((op & PythonOperationKind.InPlace) == 0) {
  1932. throw new InvalidOperationException();
  1933. }
  1934. return op & ~PythonOperationKind.InPlace;
  1935. }
  1936. private static PythonOperationKind NormalizeOperator(PythonOperationKind op) {
  1937. if ((op & PythonOperationKind.DisableCoerce) != 0) {
  1938. op = op & ~PythonOperationKind.DisableCoerce;
  1939. }
  1940. return op;
  1941. }
  1942. private static bool IsComparisonOperator(PythonOperationKind op) {
  1943. return (op & PythonOperationKind.Comparison) != 0;
  1944. }
  1945. private static bool IsComparison(PythonOperationKind op) {
  1946. return IsComparisonOperator(NormalizeOperator(op));
  1947. }
  1948. private static Expression/*!*/ GetCompareNode(PythonOperationKind op, bool reverse, Expression expr) {
  1949. op = NormalizeOperator(op);
  1950. switch (reverse ? OperatorToReverseOperator(op) : op) {
  1951. case PythonOperationKind.Equal: return Ast.Equal(expr, AstUtils.Constant(0));
  1952. case PythonOperationKind.NotEqual: return Ast.NotEqual(expr, AstUtils.Constant(0));
  1953. case PythonOperationKind.GreaterThan: return Ast.GreaterThan(expr, AstUtils.Constant(0));
  1954. case PythonOperationKind.GreaterThanOrEqual: return Ast.GreaterThanOrEqual(expr, AstUtils.Constant(0));
  1955. case PythonOperationKind.LessThan: return Ast.LessThan(expr, AstUtils.Constant(0));
  1956. case PythonOperationKind.LessThanOrEqual: return Ast.LessThanOrEqual(expr, AstUtils.Constant(0));
  1957. default: throw new InvalidOperationException();
  1958. }
  1959. }
  1960. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
  1961. public static PythonOperationKind OperatorToReverseOperator(PythonOperationKind op) {
  1962. switch (op) {
  1963. case PythonOperationKind.LessThan: return PythonOperationKind.GreaterThan;
  1964. case PythonOperationKind.LessThanOrEqual: return PythonOperationKind.GreaterThanOrEqual;
  1965. case PythonOperationKind.GreaterThan: return PythonOperationKind.LessThan;
  1966. case PythonOperationKind.GreaterThanOrEqual: return PythonOperationKind.LessThanOrEqual;
  1967. case PythonOperationKind.Equal: return PythonOperationKind.Equal;
  1968. case PythonOperationKind.NotEqual: return PythonOperationKind.NotEqual;
  1969. case PythonOperationKind.DivMod: return PythonOperationKind.ReverseDivMod;
  1970. case PythonOperationKind.ReverseDivMod: return PythonOperationKind.DivMod;
  1971. default:
  1972. return op & ~PythonOperationKind.Reversed;
  1973. }
  1974. }
  1975. private static Expression/*!*/ GetCompareExpression(PythonOperationKind op, bool reverse, Expression/*!*/ value) {
  1976. op = NormalizeOperator(op);
  1977. Debug.Assert(value.Type == typeof(int));
  1978. Expression zero = AstUtils.Constant(0);
  1979. Expression res;
  1980. switch (reverse ? OperatorToReverseOperator(op) : op) {
  1981. case PythonOperationKind.Equal: res = Ast.Equal(value, zero); break;
  1982. case PythonOperationKind.NotEqual: res = Ast.NotEqual(value, zero); break;
  1983. case PythonOperationKind.GreaterThan: res = Ast.GreaterThan(value, zero); break;
  1984. case PythonOperationKind.GreaterThanOrEqual: res = Ast.GreaterThanOrEqual(value, zero); break;
  1985. case PythonOperationKind.LessThan: res = Ast.LessThan(value, zero); break;
  1986. case PythonOperationKind.LessThanOrEqual: res = Ast.LessThanOrEqual(value, zero); break;
  1987. default: throw new InvalidOperationException();
  1988. }
  1989. return BindingHelpers.AddPythonBoxing(res);
  1990. }
  1991. private static MethodInfo/*!*/ GetComparisonFallbackMethod(PythonOperationKind op) {
  1992. op = NormalizeOperator(op);
  1993. string name;
  1994. switch (op) {
  1995. case PythonOperationKind.Equal: name = "CompareTypesEqual"; break;
  1996. case PythonOperationKind.NotEqual: name = "CompareTypesNotEqual"; break;
  1997. case PythonOperationKind.GreaterThan: name = "CompareTypesGreaterThan"; break;
  1998. case PythonOperationKind.LessThan: name = "CompareTypesLessThan"; break;
  1999. case PythonOperationKind.GreaterThanOrEqual: name = "CompareTypesGreaterThanOrEqual"; break;
  2000. case PythonOperationKind.LessThanOrEqual: name = "CompareTypesLessThanOrEqual"; break;
  2001. case PythonOperationKind.Compare: name = "CompareTypes"; break;
  2002. default: throw new InvalidOperationException();
  2003. }
  2004. return typeof(PythonOps).GetMethod(name);
  2005. }
  2006. internal static Expression/*!*/ CheckMissing(Expression/*!*/ toCheck) {
  2007. if (toCheck.Type == typeof(MissingParameter)) {
  2008. return AstUtils.Constant(null);
  2009. }
  2010. if (toCheck.Type != typeof(object)) {
  2011. return toCheck;
  2012. }
  2013. return Ast.Condition(
  2014. Ast.TypeIs(toCheck, typeof(MissingParameter)),
  2015. AstUtils.Constant(null),
  2016. toCheck
  2017. );
  2018. }
  2019. private static DynamicMetaObject/*!*/ MakeRuleForNoMatch(DynamicMetaObjectBinder/*!*/ operation, PythonOperationKind op, DynamicMetaObject errorSuggestion, params DynamicMetaObject/*!*/[]/*!*/ types) {
  2020. // we get the error message w/ {0}, {1} so that TypeError formats it correctly
  2021. return errorSuggestion ?? TypeError(
  2022. operation,
  2023. MakeBinaryOpErrorMessage(op, "{0}", "{1}"),
  2024. types);
  2025. }
  2026. internal static string/*!*/ MakeUnaryOpErrorMessage(string op, string/*!*/ xType) {
  2027. if (op == "__invert__") {
  2028. return string.Format("bad operand type for unary ~: '{0}'", xType);
  2029. } else if (op == "__abs__") {
  2030. return string.Format("bad operand type for abs(): '{0}'", xType);
  2031. } else if (op == "__pos__") {
  2032. return string.Format("bad operand type for unary +: '{0}'", xType);
  2033. } else if (op == "__neg__") {
  2034. return string.Format("bad operand type for unary -: '{0}'", xType);
  2035. }
  2036. // unreachable
  2037. throw new InvalidOperationException();
  2038. }
  2039. internal static string/*!*/ MakeBinaryOpErrorMessage(PythonOperationKind op, string/*!*/ xType, string/*!*/ yType) {
  2040. return string.Format("unsupported operand type(s) for {2}: '{0}' and '{1}'",
  2041. xType, yType, GetOperatorDisplay(op));
  2042. }
  2043. private static string/*!*/ GetOperatorDisplay(PythonOperationKind op) {
  2044. op = NormalizeOperator(op);
  2045. switch (op) {
  2046. case PythonOperationKind.Add: return "+";
  2047. case PythonOperationKind.Subtract: return "-";
  2048. case PythonOperationKind.Power: return "**";
  2049. case PythonOperationKind.Multiply: return "*";
  2050. case PythonOperationKind.FloorDivide: return "//";
  2051. case PythonOperationKind.Divide: return "/";
  2052. case PythonOperationKind.TrueDivide: return "//";
  2053. case PythonOperationKind.Mod: return "%";
  2054. case PythonOperationKind.LeftShift: return "<<";
  2055. case PythonOperationKind.RightShift: return ">>";
  2056. case PythonOperationKind.BitwiseAnd: return "&";
  2057. case PythonOperationKind.BitwiseOr: return "|";
  2058. case PythonOperationKind.ExclusiveOr: return "^";
  2059. case PythonOperationKind.LessThan: return "<";
  2060. case PythonOperationKind.GreaterThan: return ">";
  2061. case PythonOperationKind.LessThanOrEqual: return "<=";
  2062. case PythonOperationKind.GreaterThanOrEqual: return ">=";
  2063. case PythonOperationKind.Equal: return "==";
  2064. case PythonOperationKind.NotEqual: return "!=";
  2065. case PythonOperationKind.LessThanGreaterThan: return "<>";
  2066. case PythonOperationKind.InPlaceAdd: return "+=";
  2067. case PythonOperationKind.InPlaceSubtract: return "-=";
  2068. case PythonOperationKind.InPlacePower: return "**=";
  2069. case PythonOperationKind.InPlaceMultiply: return "*=";
  2070. case PythonOperationKind.InPlaceFloorDivide: return "/=";
  2071. case PythonOperationKind.InPlaceDivide: return "/=";
  2072. case PythonOperationKind.InPlaceTrueDivide: return "//=";
  2073. case PythonOperationKind.InPlaceMod: return "%=";
  2074. case PythonOperationKind.InPlaceLeftShift: return "<<=";
  2075. case PythonOperationKind.InPlaceRightShift: return ">>=";
  2076. case PythonOperationKind.InPlaceBitwiseAnd: return "&=";
  2077. case PythonOperationKind.InPlaceBitwiseOr: return "|=";
  2078. case PythonOperationKind.InPlaceExclusiveOr: return "^=";
  2079. case PythonOperationKind.ReverseAdd: return "+";
  2080. case PythonOperationKind.ReverseSubtract: return "-";
  2081. case PythonOperationKind.ReversePower: return "**";
  2082. case PythonOperationKind.ReverseMultiply: return "*";
  2083. case PythonOperationKind.ReverseFloorDivide: return "/";
  2084. case PythonOperationKind.ReverseDivide: return "/";
  2085. case PythonOperationKind.ReverseTrueDivide: return "//";
  2086. case PythonOperationKind.ReverseMod: return "%";
  2087. case PythonOperationKind.ReverseLeftShift: return "<<";
  2088. case PythonOperationKind.ReverseRightShift: return ">>";
  2089. case PythonOperationKind.ReverseBitwiseAnd: return "&";
  2090. case PythonOperationKind.ReverseBitwiseOr: return "|";
  2091. case PythonOperationKind.ReverseExclusiveOr: return "^";
  2092. default: return op.ToString();
  2093. }
  2094. }
  2095. private static DynamicMetaObject/*!*/ MakeBinaryThrow(DynamicMetaObjectBinder/*!*/ action, PythonOperationKind op, DynamicMetaObject/*!*/[]/*!*/ args) {
  2096. if (action is IPythonSite) {
  2097. // produce the custom Python error message
  2098. return new DynamicMetaObject(
  2099. action.Throw(
  2100. Ast.Call(
  2101. typeof(PythonOps).GetMethod("TypeErrorForBinaryOp"),
  2102. AstUtils.Constant(Symbols.OperatorToSymbol(NormalizeOperator(op))),
  2103. AstUtils.Convert(args[0].Expression, typeof(object)),
  2104. AstUtils.Convert(args[1].Expression, typeof(object))
  2105. ),
  2106. typeof(object)
  2107. ),
  2108. BindingRestrictions.Combine(args)
  2109. );
  2110. }
  2111. // let the site produce its own error
  2112. return GenericFallback(action, args);
  2113. }
  2114. #endregion
  2115. /// <summary>
  2116. /// Produces an error message for the provided message and type names. The error message should contain
  2117. /// string formatting characters ({0}, {1}, etc...) for each of the type names.
  2118. /// </summary>
  2119. public static DynamicMetaObject/*!*/ TypeError(DynamicMetaObjectBinder/*!*/ action, string message, params DynamicMetaObject[] types) {
  2120. if (action is IPythonSite) {
  2121. message = String.Format(message, ArrayUtils.ConvertAll(types, x => MetaPythonObject.GetPythonType(x).Name));
  2122. Expression error = action.Throw(
  2123. Ast.Call(
  2124. typeof(PythonOps).GetMethod("SimpleTypeError"),
  2125. Ast.Constant(message)
  2126. ),
  2127. typeof(object)
  2128. );
  2129. return new DynamicMetaObject(
  2130. error,
  2131. BindingRestrictions.Combine(types)
  2132. );
  2133. }
  2134. return GenericFallback(action, types);
  2135. }
  2136. private static DynamicMetaObject GenericFallback(DynamicMetaObjectBinder action, DynamicMetaObject[] types) {
  2137. if (action is GetIndexBinder) {
  2138. return ((GetIndexBinder)action).FallbackGetIndex(types[0], ArrayUtils.RemoveFirst(types));
  2139. } else if (action is SetIndexBinder) {
  2140. return ((SetIndexBinder)action).FallbackSetIndex(types[0], ArrayUtils.RemoveLast(ArrayUtils.RemoveFirst(types)), types[types.Length - 1]);
  2141. } else if (action is DeleteIndexBinder) {
  2142. return ((DeleteIndexBinder)action).FallbackDeleteIndex(types[0], ArrayUtils.RemoveFirst(types));
  2143. } else if (action is UnaryOperationBinder) {
  2144. return ((UnaryOperationBinder)action).FallbackUnaryOperation(types[0]);
  2145. } else if (action is BinaryOperationBinder) {
  2146. return ((BinaryOperationBinder)action).FallbackBinaryOperation(types[0], types[1]);
  2147. } else {
  2148. throw new NotImplementedException();
  2149. }
  2150. }
  2151. }
  2152. }