PageRenderTime 53ms CodeModel.GetById 16ms 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

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

  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Microsoft Public License, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. 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 & reveā€¦

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