PageRenderTime 61ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/IronPython_2_0/Src/IronPython/Runtime/Binding/PythonProtocol.Operations.cs

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