PageRenderTime 48ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/DICK.B1/IronPython/Runtime/Binding/PythonProtocol.Operations.cs

https://bitbucket.org/williamybs/uidipythontool
C# | 2498 lines | 1927 code | 321 blank | 250 comment | 381 complexity | 671e079969dfd580e5b24a8f9d4e150f MD5 | raw file

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

  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Microsoft Public License, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. #if !CLR2
  16. using System.Linq.Expressions;
  17. using System.Numerics;
  18. #else
  19. using Microsoft.Scripting.Math;
  20. using Complex = Microsoft.Scripting.Math.Complex64;
  21. #endif
  22. using System;
  23. using System.Collections;
  24. using System.Collections.Generic;
  25. using System.Diagnostics;
  26. using System.Dynamic;
  27. using System.Reflection;
  28. using System.Text;
  29. using Microsoft.Scripting;
  30. using Microsoft.Scripting.Actions;
  31. using Microsoft.Scripting.Actions.Calls;
  32. using Microsoft.Scripting.Ast;
  33. using Microsoft.Scripting.Generation;
  34. using Microsoft.Scripting.Runtime;
  35. using Microsoft.Scripting.Utils;
  36. using IronPython.Runtime.Operations;
  37. using IronPython.Runtime.Types;
  38. namespace IronPython.Runtime.Binding {
  39. using Ast = Expression;
  40. using AstUtils = Microsoft.Scripting.Ast.Utils;
  41. static partial class PythonProtocol {
  42. private const string DisallowCoerce = "DisallowCoerce";
  43. public static DynamicMetaObject/*!*/ Operation(BinaryOperationBinder/*!*/ operation, DynamicMetaObject target, DynamicMetaObject arg, DynamicMetaObject errorSuggestion) {
  44. PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Fallback BinaryOperator " + target.LimitType.FullName + " " + operation.Operation + " " + arg.LimitType.FullName);
  45. PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, operation.Operation.ToString());
  46. DynamicMetaObject[] args = new[] { target, arg };
  47. if (BindingHelpers.NeedsDeferral(args)) {
  48. return operation.Defer(target, arg);
  49. }
  50. ValidationInfo valInfo = BindingHelpers.GetValidationInfo(args);
  51. PythonOperationKind? pyOperator = null;
  52. switch (operation.Operation) {
  53. case ExpressionType.Add: pyOperator = PythonOperationKind.Add; break;
  54. case ExpressionType.And: pyOperator = PythonOperationKind.BitwiseAnd; break;
  55. case ExpressionType.Divide: pyOperator = PythonOperationKind.Divide; break;
  56. case ExpressionType.ExclusiveOr: pyOperator = PythonOperationKind.ExclusiveOr; break;
  57. case ExpressionType.Modulo: pyOperator = PythonOperationKind.Mod; break;
  58. case ExpressionType.Multiply: pyOperator = PythonOperationKind.Multiply; break;
  59. case ExpressionType.Or: pyOperator = PythonOperationKind.BitwiseOr; break;
  60. case ExpressionType.Power: pyOperator = PythonOperationKind.Power; break;
  61. case ExpressionType.RightShift: pyOperator = PythonOperationKind.RightShift; break;
  62. case ExpressionType.LeftShift: pyOperator = PythonOperationKind.LeftShift; break;
  63. case ExpressionType.Subtract: pyOperator = PythonOperationKind.Subtract; break;
  64. case ExpressionType.AddAssign: pyOperator = PythonOperationKind.InPlaceAdd; break;
  65. case ExpressionType.AndAssign: pyOperator = PythonOperationKind.InPlaceBitwiseAnd; break;
  66. case ExpressionType.DivideAssign: pyOperator = PythonOperationKind.InPlaceDivide; break;
  67. case ExpressionType.ExclusiveOrAssign: pyOperator = PythonOperationKind.InPlaceExclusiveOr; break;
  68. case ExpressionType.ModuloAssign: pyOperator = PythonOperationKind.InPlaceMod; break;
  69. case ExpressionType.MultiplyAssign: pyOperator = PythonOperationKind.InPlaceMultiply; break;
  70. case ExpressionType.OrAssign: pyOperator = PythonOperationKind.InPlaceBitwiseOr; break;
  71. case ExpressionType.PowerAssign: pyOperator = PythonOperationKind.InPlacePower; break;
  72. case ExpressionType.RightShiftAssign: pyOperator = PythonOperationKind.InPlaceRightShift; break;
  73. case ExpressionType.LeftShiftAssign: pyOperator = PythonOperationKind.InPlaceLeftShift; break;
  74. case ExpressionType.SubtractAssign: pyOperator = PythonOperationKind.InPlaceSubtract; break;
  75. case ExpressionType.Equal: pyOperator = PythonOperationKind.Equal; break;
  76. case ExpressionType.GreaterThan: pyOperator = PythonOperationKind.GreaterThan; break;
  77. case ExpressionType.GreaterThanOrEqual: pyOperator = PythonOperationKind.GreaterThanOrEqual; break;
  78. case ExpressionType.LessThan: pyOperator = PythonOperationKind.LessThan; break;
  79. case ExpressionType.LessThanOrEqual: pyOperator = PythonOperationKind.LessThanOrEqual; break;
  80. case ExpressionType.NotEqual: pyOperator = PythonOperationKind.NotEqual; break;
  81. }
  82. DynamicMetaObject res = null;
  83. if (pyOperator != null) {
  84. res = MakeBinaryOperation(operation, args, pyOperator.Value, errorSuggestion);
  85. } else {
  86. res = operation.FallbackBinaryOperation(target, arg);
  87. }
  88. return BindingHelpers.AddDynamicTestAndDefer(operation, BindingHelpers.AddPythonBoxing(res), args, valInfo);
  89. }
  90. public static DynamicMetaObject/*!*/ Operation(UnaryOperationBinder/*!*/ operation, DynamicMetaObject arg, DynamicMetaObject errorSuggestion) {
  91. PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Fallback UnaryOperator " + " " + operation.Operation + " " + arg.LimitType.FullName);
  92. PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, operation.Operation.ToString());
  93. DynamicMetaObject[] args = new[] { arg };
  94. if (arg.NeedsDeferral()) {
  95. return operation.Defer(arg);
  96. }
  97. ValidationInfo valInfo = BindingHelpers.GetValidationInfo(args);
  98. DynamicMetaObject res = null;
  99. Type retType = typeof(object);
  100. switch (operation.Operation) {
  101. case ExpressionType.UnaryPlus:
  102. res = BindingHelpers.AddPythonBoxing(MakeUnaryOperation(operation, arg, "__pos__", errorSuggestion));
  103. break;
  104. case ExpressionType.Negate:
  105. res = BindingHelpers.AddPythonBoxing(MakeUnaryOperation(operation, arg, "__neg__", errorSuggestion));
  106. break;
  107. case ExpressionType.OnesComplement:
  108. res = BindingHelpers.AddPythonBoxing(MakeUnaryOperation(operation, arg, "__invert__", errorSuggestion));
  109. break;
  110. case ExpressionType.Not:
  111. res = MakeUnaryNotOperation(operation, arg, typeof(object), errorSuggestion);
  112. break;
  113. case ExpressionType.IsFalse:
  114. res = MakeUnaryNotOperation(operation, arg, typeof(bool), errorSuggestion);
  115. retType = typeof(bool);
  116. break;
  117. case ExpressionType.IsTrue:
  118. res = PythonProtocol.ConvertToBool(operation, arg);
  119. retType = typeof(bool);
  120. break;
  121. default:
  122. res = TypeError(operation, "unknown operation: " + operation.ToString(), args);
  123. break;
  124. }
  125. return BindingHelpers.AddDynamicTestAndDefer(operation, res, args, valInfo, retType);
  126. }
  127. public static DynamicMetaObject/*!*/ Index(DynamicMetaObjectBinder/*!*/ operation, PythonIndexType index, DynamicMetaObject[] args) {
  128. return Index(operation, index, args, null);
  129. }
  130. public static DynamicMetaObject/*!*/ Index(DynamicMetaObjectBinder/*!*/ operation, PythonIndexType index, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion) {
  131. if (args.Length >= 3) {
  132. PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Fallback Index " + " " + index + " " + args[0].LimitType + ", " + args[1].LimitType + ", " + args[2].LimitType + args.Length);
  133. } else {
  134. PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Fallback Index " + " " + index + " " + args[0].LimitType + ", " + args[1].LimitType + args.Length);
  135. }
  136. PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, index.ToString());
  137. if (BindingHelpers.NeedsDeferral(args)) {
  138. return operation.Defer(args);
  139. }
  140. ValidationInfo valInfo = BindingHelpers.GetValidationInfo(args[0]);
  141. DynamicMetaObject res = BindingHelpers.AddPythonBoxing(MakeIndexerOperation(operation, index, args, errorSuggestion));
  142. return BindingHelpers.AddDynamicTestAndDefer(operation, res, args, valInfo);
  143. }
  144. public static DynamicMetaObject/*!*/ Operation(PythonOperationBinder/*!*/ operation, params DynamicMetaObject/*!*/[]/*!*/ args) {
  145. if (args.Length == 1) {
  146. PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Fallback PythonOp " + " " + operation.Operation + " " + args[0].LimitType);
  147. } else {
  148. PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Fallback PythonOp " + " " + operation.Operation + " " + args[0].LimitType + ", " + args[1].LimitType);
  149. }
  150. PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, operation.Operation.ToString());
  151. if (BindingHelpers.NeedsDeferral(args)) {
  152. return operation.Defer(args);
  153. }
  154. return MakeOperationRule(operation, args);
  155. }
  156. private static DynamicMetaObject/*!*/ MakeOperationRule(PythonOperationBinder/*!*/ operation, DynamicMetaObject/*!*/[]/*!*/ args) {
  157. ValidationInfo valInfo = BindingHelpers.GetValidationInfo(args);
  158. DynamicMetaObject res;
  159. Type deferType = typeof(object);
  160. switch (NormalizeOperator(operation.Operation)) {
  161. case PythonOperationKind.Documentation:
  162. res = BindingHelpers.AddPythonBoxing(MakeDocumentationOperation(operation, args));
  163. break;
  164. case PythonOperationKind.CallSignatures:
  165. res = BindingHelpers.AddPythonBoxing(MakeCallSignatureOperation(args[0], CompilerHelpers.GetMethodTargets(args[0].Value)));
  166. break;
  167. case PythonOperationKind.IsCallable:
  168. res = MakeIscallableOperation(operation, args);
  169. break;
  170. case PythonOperationKind.Hash:
  171. res = MakeHashOperation(operation, args[0]);
  172. break;
  173. case PythonOperationKind.Contains:
  174. res = MakeContainsOperation(operation, args);
  175. break;
  176. case PythonOperationKind.AbsoluteValue:
  177. res = BindingHelpers.AddPythonBoxing(MakeUnaryOperation(operation, args[0], "__abs__", null));
  178. break;
  179. case PythonOperationKind.Compare:
  180. res = MakeSortComparisonRule(args, operation, operation.Operation);
  181. Debug.Assert(res.LimitType == typeof(int));
  182. break;
  183. case PythonOperationKind.GetEnumeratorForIteration:
  184. res = MakeEnumeratorOperation(operation, args[0]);
  185. break;
  186. default:
  187. res = BindingHelpers.AddPythonBoxing(MakeBinaryOperation(operation, args, operation.Operation, null));
  188. break;
  189. }
  190. return BindingHelpers.AddDynamicTestAndDefer(operation, res, args, valInfo, deferType);
  191. }
  192. private static DynamicMetaObject MakeBinaryOperation(DynamicMetaObjectBinder operation, DynamicMetaObject/*!*/[] args, PythonOperationKind opStr, DynamicMetaObject errorSuggestion) {
  193. if (IsComparison(opStr)) {
  194. return MakeComparisonOperation(args, operation, opStr, errorSuggestion);
  195. }
  196. return MakeSimpleOperation(args, operation, opStr, errorSuggestion);
  197. }
  198. #region Unary Operations
  199. /// <summary>
  200. /// Creates a rule for the contains operator. This is exposed via "x in y" in
  201. /// IronPython. It is implemented by calling the __contains__ method on x and
  202. /// passing in y.
  203. ///
  204. /// If a type doesn't define __contains__ but does define __getitem__ then __getitem__ is
  205. /// called repeatedly in order to see if the object is there.
  206. ///
  207. /// For normal .NET enumerables we'll walk the iterator and see if it's present.
  208. /// </summary>
  209. private static DynamicMetaObject/*!*/ MakeContainsOperation(PythonOperationBinder/*!*/ operation, DynamicMetaObject/*!*/[]/*!*/ types) {
  210. DynamicMetaObject res;
  211. // the paramteres come in backwards from how we look up __contains__, flip them.
  212. Debug.Assert(types.Length == 2);
  213. ArrayUtils.SwapLastTwo(types);
  214. PythonContext state = PythonContext.GetPythonContext(operation);
  215. SlotOrFunction sf = SlotOrFunction.GetSlotOrFunction(state, "__contains__", types);
  216. if (sf.Success) {
  217. // just a call to __contains__
  218. res = sf.Target;
  219. } else {
  220. RestrictTypes(types);
  221. sf = SlotOrFunction.GetSlotOrFunction(state, "__iter__", types[0]);
  222. if (sf.Success) {
  223. // iterate using __iter__
  224. res = new DynamicMetaObject(
  225. Ast.Call(
  226. typeof(PythonOps).GetMethod("ContainsFromEnumerable"),
  227. AstUtils.Constant(state.SharedContext),
  228. sf.Target.Expression,
  229. AstUtils.Convert(types[1].Expression, typeof(object))
  230. ),
  231. BindingRestrictions.Combine(types)
  232. );
  233. } else {
  234. ParameterExpression curIndex = Ast.Variable(typeof(int), "count");
  235. sf = SlotOrFunction.GetSlotOrFunction(state, "__getitem__", types[0], new DynamicMetaObject(curIndex, BindingRestrictions.Empty));
  236. if (sf.Success) {
  237. // defines __getitem__, need to loop over the indexes and see if we match
  238. ParameterExpression getItemRes = Ast.Variable(sf.ReturnType, "getItemRes");
  239. ParameterExpression containsRes = Ast.Variable(typeof(bool), "containsRes");
  240. LabelTarget target = Ast.Label();
  241. res = new DynamicMetaObject(
  242. Ast.Block(
  243. new ParameterExpression[] { curIndex, getItemRes, containsRes },
  244. Utils.Loop(
  245. null, // test
  246. Ast.Assign(curIndex, Ast.Add(curIndex, AstUtils.Constant(1))), // increment
  247. Ast.Block( // body
  248. // getItemRes = param0.__getitem__(curIndex)
  249. Utils.Try(
  250. Ast.Block(
  251. Ast.Assign(
  252. getItemRes,
  253. sf.Target.Expression
  254. ),
  255. Ast.Empty()
  256. )
  257. ).Catch(
  258. // end of indexes, return false
  259. typeof(IndexOutOfRangeException),
  260. Ast.Break(target)
  261. ),
  262. // if(getItemRes == param1) return true
  263. Utils.If(
  264. Ast.Dynamic(
  265. state.BinaryOperationRetType(
  266. state.BinaryOperation(ExpressionType.Equal),
  267. state.Convert(typeof(bool), ConversionResultKind.ExplicitCast)
  268. ),
  269. typeof(bool),
  270. types[1].Expression,
  271. getItemRes
  272. ),
  273. Ast.Assign(containsRes, AstUtils.Constant(true)),
  274. Ast.Break(target)
  275. ),
  276. AstUtils.Empty()
  277. ),
  278. null, // loop else
  279. target, // break label target
  280. null
  281. ),
  282. containsRes
  283. ),
  284. BindingRestrictions.Combine(types)
  285. );
  286. } else {
  287. // non-iterable object
  288. res = new DynamicMetaObject(
  289. Ast.Throw(
  290. Ast.Call(
  291. typeof(PythonOps).GetMethod("TypeErrorForNonIterableObject"),
  292. AstUtils.Convert(
  293. types[1].Expression,
  294. typeof(object)
  295. )
  296. ),
  297. typeof(bool)
  298. ),
  299. BindingRestrictions.Combine(types)
  300. );
  301. }
  302. }
  303. }
  304. if (res.GetLimitType() != typeof(bool) && res.GetLimitType() != typeof(void)) {
  305. res = new DynamicMetaObject(
  306. Ast.Dynamic(
  307. state.Convert(
  308. typeof(bool),
  309. ConversionResultKind.ExplicitCast
  310. ),
  311. typeof(bool),
  312. res.Expression
  313. ),
  314. res.Restrictions
  315. );
  316. }
  317. return res;
  318. }
  319. private static void RestrictTypes(DynamicMetaObject/*!*/[] types) {
  320. for (int i = 0; i < types.Length; i++) {
  321. types[i] = types[i].Restrict(types[i].GetLimitType());
  322. }
  323. }
  324. private static DynamicMetaObject/*!*/ MakeHashOperation(PythonOperationBinder/*!*/ operation, DynamicMetaObject/*!*/ self) {
  325. self = self.Restrict(self.GetLimitType());
  326. PythonContext state = PythonContext.GetPythonContext(operation);
  327. SlotOrFunction func = SlotOrFunction.GetSlotOrFunction(state, "__hash__", self);
  328. DynamicMetaObject res = func.Target;
  329. if (func.IsNull) {
  330. // Python 2.6 setting __hash__ = None makes the type unhashable
  331. res = new DynamicMetaObject(
  332. Expression.Throw(
  333. Expression.Call(
  334. typeof(PythonOps).GetMethod("TypeErrorForUnhashableObject"),
  335. self.Expression
  336. ),
  337. typeof(int)
  338. ),
  339. res.Restrictions
  340. );
  341. } else if (func.ReturnType != typeof(int)) {
  342. if (func.ReturnType == typeof(BigInteger)) {
  343. // Python 2.5 defines the result of returning a long as hashing the long
  344. res = new DynamicMetaObject(
  345. HashBigInt(operation, res.Expression),
  346. res.Restrictions
  347. );
  348. } else if (func.ReturnType == typeof(object)) {
  349. // need to get the integer value here...
  350. ParameterExpression tempVar = Ast.Parameter(typeof(object), "hashTemp");
  351. res = new DynamicMetaObject(
  352. Expression.Block(
  353. new[] { tempVar },
  354. Expression.Assign(tempVar, res.Expression),
  355. Expression.Condition(
  356. Expression.TypeIs(tempVar, typeof(int)),
  357. Expression.Convert(tempVar, typeof(int)),
  358. Expression.Condition(
  359. Expression.TypeIs(tempVar, typeof(BigInteger)),
  360. HashBigInt(operation, tempVar),
  361. HashConvertToInt(state, tempVar)
  362. )
  363. )
  364. ),
  365. res.Restrictions
  366. );
  367. } else {
  368. // need to convert unknown value to object
  369. res = new DynamicMetaObject(
  370. HashConvertToInt(state, res.Expression),
  371. res.Restrictions
  372. );
  373. }
  374. }
  375. return res;
  376. }
  377. private static DynamicExpression/*!*/ HashBigInt(PythonOperationBinder/*!*/ operation, Expression/*!*/ expression) {
  378. return Ast.Dynamic(
  379. operation,
  380. typeof(int),
  381. expression
  382. );
  383. }
  384. private static DynamicExpression/*!*/ HashConvertToInt(PythonContext/*!*/ state, Expression/*!*/ expression) {
  385. return Ast.Dynamic(
  386. state.Convert(
  387. typeof(int),
  388. ConversionResultKind.ExplicitCast
  389. ),
  390. typeof(int),
  391. expression
  392. );
  393. }
  394. private static DynamicMetaObject MakeUnaryOperation(DynamicMetaObjectBinder binder, DynamicMetaObject self, string symbol, DynamicMetaObject errorSuggestion) {
  395. self = self.Restrict(self.GetLimitType());
  396. SlotOrFunction func = SlotOrFunction.GetSlotOrFunction(PythonContext.GetPythonContext(binder), symbol, self);
  397. if (!func.Success) {
  398. // we get the error message w/ {0} so that PythonBinderHelper.TypeError formats it correctly
  399. return errorSuggestion ?? TypeError(binder, MakeUnaryOpErrorMessage(symbol, "{0}"), self);
  400. }
  401. return func.Target;
  402. }
  403. private static DynamicMetaObject MakeEnumeratorOperation(PythonOperationBinder operation, DynamicMetaObject self) {
  404. if (self.GetLimitType() == typeof(string)) {
  405. self = self.Restrict(self.GetLimitType());
  406. return new DynamicMetaObject(
  407. Expression.Call(
  408. typeof(PythonOps).GetMethod("StringEnumerator"),
  409. self.Expression
  410. ),
  411. self.Restrictions
  412. );
  413. } else if (self.GetLimitType() == typeof(Bytes)) {
  414. self = self.Restrict(self.GetLimitType());
  415. if (operation.Context.PythonOptions.Python30) {
  416. return new DynamicMetaObject(
  417. Expression.Call(
  418. typeof(PythonOps).GetMethod("BytesIntEnumerator"),
  419. self.Expression
  420. ),
  421. self.Restrictions
  422. );
  423. } else {
  424. return new DynamicMetaObject(
  425. Expression.Call(
  426. typeof(PythonOps).GetMethod("BytesEnumerator"),
  427. self.Expression
  428. ),
  429. self.Restrictions
  430. );
  431. }
  432. } else if ((self.Value is IEnumerable ||
  433. typeof(IEnumerable).IsAssignableFrom(self.GetLimitType())) && !(self.Value is PythonGenerator)) {
  434. self = self.Restrict(self.GetLimitType());
  435. return new DynamicMetaObject(
  436. Expression.Call(
  437. typeof(PythonOps).GetMethod("GetEnumeratorFromEnumerable"),
  438. Expression.Convert(
  439. self.Expression,
  440. typeof(IEnumerable)
  441. )
  442. ),
  443. self.Restrictions
  444. );
  445. } else if (self.Value is IEnumerator || // check for COM object (and fast check when we have values)
  446. typeof(IEnumerator).IsAssignableFrom(self.GetLimitType())) { // check if we don't have a value
  447. DynamicMetaObject ieres = new DynamicMetaObject(
  448. MakeEnumeratorResult(
  449. Ast.Convert(
  450. self.Expression,
  451. typeof(IEnumerator)
  452. )
  453. ),
  454. self.Restrict(self.GetLimitType()).Restrictions
  455. );
  456. #if !SILVERLIGHT
  457. if (Microsoft.Scripting.ComInterop.ComBinder.IsComObject(self.Value)) {
  458. ieres = new DynamicMetaObject(
  459. MakeEnumeratorResult(
  460. Expression.Convert(
  461. self.Expression,
  462. typeof(IEnumerator)
  463. )
  464. ),
  465. ieres.Restrictions.Merge(
  466. BindingRestrictions.GetExpressionRestriction(
  467. Ast.TypeIs(self.Expression, typeof(IEnumerator))
  468. )
  469. )
  470. );
  471. }
  472. #endif
  473. return ieres;
  474. }
  475. ParameterExpression tmp = Ast.Parameter(typeof(IEnumerator), "enum");
  476. IPythonConvertible pyConv = self as IPythonConvertible;
  477. PythonConversionBinder convBinder = PythonContext.GetPythonContext(operation).Convert(typeof(IEnumerator), ConversionResultKind.ExplicitTry);
  478. DynamicMetaObject res;
  479. if (pyConv != null) {
  480. res = pyConv.BindConvert(convBinder);
  481. } else {
  482. res = convBinder.Bind(self, new DynamicMetaObject[0]);
  483. }
  484. return new DynamicMetaObject(
  485. Expression.Block(
  486. new[] { tmp },
  487. Ast.Condition(
  488. Ast.NotEqual(
  489. Ast.Assign(tmp, res.Expression),
  490. AstUtils.Constant(null)
  491. ),
  492. MakeEnumeratorResult(tmp),
  493. Ast.Call(
  494. typeof(PythonOps).GetMethod("ThrowTypeErrorForBadIteration"),
  495. PythonContext.GetCodeContext(operation),
  496. self.Expression
  497. )
  498. )
  499. ),
  500. res.Restrictions
  501. );
  502. }
  503. private static NewExpression MakeEnumeratorResult(Expression tmp) {
  504. return Expression.New(
  505. typeof(KeyValuePair<IEnumerator, IDisposable>).GetConstructor(new[] { typeof(IEnumerator), typeof(IDisposable) }),
  506. tmp,
  507. Expression.Constant(null, typeof(IDisposable))
  508. );
  509. }
  510. private static DynamicMetaObject/*!*/ MakeUnaryNotOperation(DynamicMetaObjectBinder/*!*/ operation, DynamicMetaObject/*!*/ self, Type retType, DynamicMetaObject errorSuggestion) {
  511. self = self.Restrict(self.GetLimitType());
  512. SlotOrFunction nonzero = SlotOrFunction.GetSlotOrFunction(PythonContext.GetPythonContext(operation), "__nonzero__", self);
  513. SlotOrFunction length = SlotOrFunction.GetSlotOrFunction(PythonContext.GetPythonContext(operation), "__len__", self);
  514. Expression notExpr;
  515. if (!nonzero.Success && !length.Success) {
  516. // no __len__ or __nonzero__, for None this is always false, everything else is True. If we have
  517. // an error suggestion though we'll go with that.
  518. if (errorSuggestion == null) {
  519. notExpr = (self.GetLimitType() == typeof(DynamicNull)) ? AstUtils.Constant(true) : AstUtils.Constant(false);
  520. } else {
  521. notExpr = errorSuggestion.Expression;
  522. }
  523. } else {
  524. SlotOrFunction target = nonzero.Success ? nonzero : length;
  525. notExpr = target.Target.Expression;
  526. if (nonzero.Success) {
  527. // call non-zero and negate it
  528. if (notExpr.Type == typeof(bool)) {
  529. notExpr = Ast.Equal(notExpr, AstUtils.Constant(false));
  530. } else {
  531. notExpr = Ast.Call(
  532. typeof(PythonOps).GetMethod("Not"),
  533. AstUtils.Convert(notExpr, typeof(object))
  534. );
  535. }
  536. } else {
  537. // call len, compare w/ zero
  538. if (notExpr.Type == typeof(int)) {
  539. notExpr = Ast.Equal(notExpr, AstUtils.Constant(0));
  540. } else {
  541. notExpr =
  542. Ast.Equal(
  543. Ast.Dynamic(
  544. PythonContext.GetPythonContext(operation).Operation(
  545. PythonOperationKind.Compare
  546. ),
  547. typeof(int),
  548. notExpr,
  549. AstUtils.Constant(0)
  550. ),
  551. AstUtils.Constant(0)
  552. );
  553. }
  554. }
  555. }
  556. if (retType == typeof(object) && notExpr.Type == typeof(bool)) {
  557. notExpr = BindingHelpers.AddPythonBoxing(notExpr);
  558. }
  559. return new DynamicMetaObject(
  560. notExpr,
  561. self.Restrictions.Merge(nonzero.Target.Restrictions.Merge(length.Target.Restrictions))
  562. );
  563. }
  564. #endregion
  565. #region Reflective Operations
  566. private static DynamicMetaObject/*!*/ MakeDocumentationOperation(PythonOperationBinder/*!*/ operation, DynamicMetaObject/*!*/[]/*!*/ args) {
  567. PythonContext state = PythonContext.GetPythonContext(operation);
  568. return new DynamicMetaObject(
  569. Binders.Convert(
  570. PythonContext.GetCodeContext(operation),
  571. state,
  572. typeof(string),
  573. ConversionResultKind.ExplicitCast,
  574. Binders.Get(
  575. PythonContext.GetCodeContext(operation),
  576. state,
  577. typeof(object),
  578. "__doc__",
  579. args[0].Expression
  580. )
  581. ),
  582. args[0].Restrictions
  583. );
  584. }
  585. internal static DynamicMetaObject/*!*/ MakeCallSignatureOperation(DynamicMetaObject/*!*/ self, IList<MethodBase/*!*/>/*!*/ targets) {
  586. List<string> arrres = new List<string>();
  587. foreach (MethodBase mb in targets) {
  588. StringBuilder res = new StringBuilder();
  589. string comma = "";
  590. Type retType = mb.GetReturnType();
  591. if (retType != typeof(void)) {
  592. res.Append(DynamicHelpers.GetPythonTypeFromType(retType).Name);
  593. res.Append(" ");
  594. }
  595. MethodInfo mi = mb as MethodInfo;
  596. if (mi != null) {
  597. string name;
  598. NameConverter.TryGetName(DynamicHelpers.GetPythonTypeFromType(mb.DeclaringType), mi, out name);
  599. res.Append(name);
  600. } else {
  601. res.Append(DynamicHelpers.GetPythonTypeFromType(mb.DeclaringType).Name);
  602. }
  603. res.Append("(");
  604. if (!CompilerHelpers.IsStatic(mb)) {
  605. res.Append("self");
  606. comma = ", ";
  607. }
  608. foreach (ParameterInfo pi in mb.GetParameters()) {
  609. if (pi.ParameterType == typeof(CodeContext)) continue;
  610. res.Append(comma);
  611. res.Append(DynamicHelpers.GetPythonTypeFromType(pi.ParameterType).Name + " " + pi.Name);
  612. comma = ", ";
  613. }
  614. res.Append(")");
  615. arrres.Add(res.ToString());
  616. }
  617. return new DynamicMetaObject(
  618. AstUtils.Constant(arrres.ToArray()),
  619. self.Restrictions.Merge(BindingRestrictions.GetInstanceRestriction(self.Expression, self.Value))
  620. );
  621. }
  622. private static DynamicMetaObject/*!*/ MakeIscallableOperation(PythonOperationBinder/*!*/ operation, DynamicMetaObject/*!*/[]/*!*/ args) {
  623. // Certain non-python types (encountered during interop) are callable, but don't have
  624. // a __call__ attribute. The default base binder also checks these, but since we're overriding
  625. // the base binder, we check them here.
  626. DynamicMetaObject self = args[0];
  627. // only applies when called from a Python site
  628. if (typeof(Delegate).IsAssignableFrom(self.GetLimitType()) ||
  629. typeof(MethodGroup).IsAssignableFrom(self.GetLimitType())) {
  630. return new DynamicMetaObject(
  631. AstUtils.Constant(true),
  632. self.Restrict(self.GetLimitType()).Restrictions
  633. );
  634. }
  635. PythonContext state = PythonContext.GetPythonContext(operation);
  636. Expression isCallable = Ast.NotEqual(
  637. Binders.TryGet(
  638. PythonContext.GetCodeContext(operation),
  639. state,
  640. typeof(object),
  641. "__call__",
  642. self.Expression
  643. ),
  644. AstUtils.Constant(OperationFailed.Value)
  645. );
  646. return new DynamicMetaObject(
  647. isCallable,
  648. self.Restrict(self.GetLimitType()).Restrictions
  649. );
  650. }
  651. #endregion
  652. #region Common Binary Operations
  653. private static DynamicMetaObject/*!*/ MakeSimpleOperation(DynamicMetaObject/*!*/[]/*!*/ types, DynamicMetaObjectBinder/*!*/ binder, PythonOperationKind operation, DynamicMetaObject errorSuggestion) {
  654. RestrictTypes(types);
  655. SlotOrFunction fbinder;
  656. SlotOrFunction rbinder;
  657. PythonTypeSlot fSlot;
  658. PythonTypeSlot rSlot;
  659. GetOpreatorMethods(types, operation, PythonContext.GetPythonContext(binder), out fbinder, out rbinder, out fSlot, out rSlot);
  660. return MakeBinaryOperatorResult(types, binder, operation, fbinder, rbinder, fSlot, rSlot, errorSuggestion);
  661. }
  662. private static void GetOpreatorMethods(DynamicMetaObject/*!*/[]/*!*/ types, PythonOperationKind oper, PythonContext state, out SlotOrFunction fbinder, out SlotOrFunction rbinder, out PythonTypeSlot fSlot, out PythonTypeSlot rSlot) {
  663. oper = NormalizeOperator(oper);
  664. oper &= ~PythonOperationKind.InPlace;
  665. string op, rop;
  666. if (!IsReverseOperator(oper)) {
  667. op = Symbols.OperatorToSymbol(oper);
  668. rop = Symbols.OperatorToReversedSymbol(oper);
  669. } else {
  670. // coming back after coercion, just try reverse operator.
  671. rop = Symbols.OperatorToSymbol(oper);
  672. op = Symbols.OperatorToReversedSymbol(oper);
  673. }
  674. fSlot = null;
  675. rSlot = null;
  676. PythonType fParent, rParent;
  677. if (oper == PythonOperationKind.Multiply &&
  678. IsSequence(types[0]) &&
  679. !PythonOps.IsNonExtensibleNumericType(types[1].GetLimitType())) {
  680. // class M:
  681. // def __rmul__(self, other):
  682. // print "CALLED"
  683. // return 1
  684. //
  685. // print [1,2] * M()
  686. //
  687. // in CPython this results in a successful call to __rmul__ on the type ignoring the forward
  688. // multiplication. But calling the __mul__ method directly does NOT return NotImplemented like
  689. // one might expect. Therefore we explicitly convert the MetaObject argument into an Index
  690. // for binding purposes. That allows this to work at multiplication time but not with
  691. // a direct call to __mul__.
  692. DynamicMetaObject[] newTypes = new DynamicMetaObject[2];
  693. newTypes[0] = types[0];
  694. newTypes[1] = new DynamicMetaObject(
  695. Ast.New(
  696. typeof(Index).GetConstructor(new Type[] { typeof(object) }),
  697. AstUtils.Convert(types[1].Expression, typeof(object))
  698. ),
  699. BindingRestrictions.Empty
  700. );
  701. types = newTypes;
  702. }
  703. if (!SlotOrFunction.TryGetBinder(state, types, op, null, out fbinder, out fParent)) {
  704. foreach (PythonType pt in MetaPythonObject.GetPythonType(types[0]).ResolutionOrder) {
  705. if (pt.TryLookupSlot(state.SharedContext, op, out fSlot)) {
  706. fParent = pt;
  707. break;
  708. }
  709. }
  710. }
  711. if (!SlotOrFunction.TryGetBinder(state, types, null, rop, out rbinder, out rParent)) {
  712. foreach (PythonType pt in MetaPythonObject.GetPythonType(types[1]).ResolutionOrder) {
  713. if (pt.TryLookupSlot(state.SharedContext, rop, out rSlot)) {
  714. rParent = pt;
  715. break;
  716. }
  717. }
  718. }
  719. if (fParent != null && (rbinder.Success || rSlot != null) && rParent != fParent && rParent.IsSubclassOf(fParent)) {
  720. // Python says if x + subx and subx defines __r*__ we should call r*.
  721. fbinder = SlotOrFunction.Empty;
  722. fSlot = null;
  723. }
  724. if (!fbinder.Success && !rbinder.Success && fSlot == null && rSlot == null) {
  725. if (op == "__truediv__" || op == "__rtruediv__") {
  726. // true div on a type which doesn't support it, go ahead and try normal divide
  727. PythonOperationKind newOp = op == "__truediv__" ? PythonOperationKind.Divide : PythonOperationKind.ReverseDivide;
  728. GetOpreatorMethods(types, newOp, state, out fbinder, out rbinder, out fSlot, out rSlot);
  729. }
  730. }
  731. }
  732. private static bool IsReverseOperator(PythonOperationKind oper) {
  733. return (oper & PythonOperationKind.Reversed) != 0;
  734. }
  735. private static bool IsSequence(DynamicMetaObject/*!*/ metaObject) {
  736. if (typeof(List).IsAssignableFrom(metaObject.GetLimitType()) ||
  737. typeof(PythonTuple).IsAssignableFrom(metaObject.GetLimitType()) ||
  738. typeof(String).IsAssignableFrom(metaObject.GetLimitType())) {
  739. return true;
  740. }
  741. return false;
  742. }
  743. private static DynamicMetaObject/*!*/ MakeBinaryOperatorResult(DynamicMetaObject/*!*/[]/*!*/ types, DynamicMetaObjectBinder/*!*/ operation, PythonOperationKind op, SlotOrFunction/*!*/ fCand, SlotOrFunction/*!*/ rCand, PythonTypeSlot fSlot, PythonTypeSlot rSlot, DynamicMetaObject errorSuggestion) {
  744. Assert.NotNull(operation, fCand, rCand);
  745. SlotOrFunction fTarget, rTarget;
  746. PythonContext state = PythonContext.GetPythonContext(operation);
  747. ConditionalBuilder bodyBuilder = new ConditionalBuilder(operation);
  748. if ((op & PythonOperationKind.InPlace) != 0) {
  749. // in place operator, see if there's a specific method that handles it.
  750. SlotOrFunction function = SlotOrFunction.GetSlotOrFunction(PythonContext.GetPythonContext(operation), Symbols.OperatorToSymbol(op), types);
  751. // we don't do a coerce for in place operators if the lhs implements __iop__
  752. if (!MakeOneCompareGeneric(function, false, types, MakeCompareReturn, bodyBuilder, typeof(object))) {
  753. // the method handles it and always returns a useful value.
  754. return bodyBuilder.GetMetaObject(types);
  755. }
  756. }
  757. if (!SlotOrFunction.GetCombinedTargets(fCand, rCand, out fTarget, out rTarget) &&
  758. fSlot == null &&
  759. rSlot == null &&
  760. !ShouldCoerce(state, op, types[0], types[1], false) &&
  761. !ShouldCoerce(state, op, types[1], types[0], false) &&
  762. bodyBuilder.NoConditions) {
  763. return MakeRuleForNoMatch(operation, op, errorSuggestion, types);
  764. }
  765. if (ShouldCoerce(state, op, types[0], types[1], false) &&
  766. (op != PythonOperationKind.Mod || !MetaPythonObject.GetPythonType(types[0]).IsSubclassOf(TypeCache.String))) {
  767. // need to try __coerce__ first.
  768. DoCoerce(state, bodyBuilder, op, types, false);
  769. }
  770. if (MakeOneTarget(PythonContext.GetPythonContext(operation), fTarget, fSlot, bodyBuilder, false, types)) {
  771. if (ShouldCoerce(state, op, types[1], types[0], false)) {
  772. // need to try __coerce__ on the reverse first
  773. DoCoerce(state, bodyBuilder, op, new DynamicMetaObject[] { types[1], types[0] }, true);
  774. }
  775. if (rSlot != null) {
  776. MakeSlotCall(PythonContext.GetPythonContext(operation), types, bodyBuilder, rSlot, true);
  777. bodyBuilder.FinishCondition(MakeBinaryThrow(operation, op, types).Expression, typeof(object));
  778. } else if (MakeOneTarget(PythonContext.GetPythonContext(operation), rTarget, rSlot, bodyBuilder, false, types)) {
  779. // need to fallback to throwing or coercion
  780. bodyBuilder.FinishCondition(MakeBinaryThrow(operation, op, types).Expression, typeof(object));
  781. }
  782. }
  783. return bodyBuilder.GetMetaObject(types);
  784. }
  785. private static void MakeCompareReturn(ConditionalBuilder/*!*/ bodyBuilder, Expression retCondition, Expression/*!*/ retValue, bool isReverse, Type retType) {
  786. if (retCondition != null) {
  787. bodyBuilder.AddCondition(retCondition, retValue);
  788. } else {
  789. bodyBuilder.FinishCondition(retValue, retType);
  790. }
  791. }
  792. /// <summary>
  793. /// Delegate for finishing the comparison. This takes in a condition and a return value and needs to update the ConditionalBuilder
  794. /// with the appropriate resulting body. The condition may be null.
  795. /// </summary>
  796. private delegate void ComparisonHelper(ConditionalBuilder/*!*/ bodyBuilder, Expression retCondition, Expression/*!*/ retValue, bool isReverse, Type retType);
  797. /// <summary>
  798. /// Helper to handle a comparison operator call. Checks to see if the call can
  799. /// return NotImplemented and allows the caller to modify the expression that
  800. /// is ultimately returned (e.g. to turn __cmp__ into a bool after a comparison)
  801. /// </summary>
  802. private static bool MakeOneCompareGeneric(SlotOrFunction/*!*/ target, bool reverse, DynamicMetaObject/*!*/[]/*!*/ types, ComparisonHelper returner, ConditionalBuilder/*!*/ bodyBuilder, Type retType) {
  803. if (target == SlotOrFunction.Empty || !target.Success) return true;
  804. ParameterExpression tmp;
  805. if (target.ReturnType == typeof(bool)) {
  806. tmp = bodyBuilder.CompareRetBool;
  807. } else {
  808. tmp = Ast.Variable(target.ReturnType, "compareRetValue");
  809. bodyBuilder.AddVariable(tmp);
  810. }
  811. if (target.MaybeNotImplemented) {
  812. Expression call = target.Target.Expression;
  813. Expression assign = Ast.Assign(tmp, call);
  814. returner(
  815. bodyBuilder,
  816. Ast.NotEqual(
  817. assign,
  818. AstUtils.Constant(PythonOps.NotImplemented)
  819. ),
  820. tmp,
  821. reverse,
  822. retType);
  823. return true;
  824. } else {
  825. returner(
  826. bodyBuilder,
  827. null,
  828. target.Target.Expression,
  829. reverse,
  830. retType
  831. );
  832. return false;
  833. }
  834. }
  835. private static bool MakeOneTarget(PythonContext/*!*/ state, SlotOrFunction/*!*/ target, PythonTypeSlot slotTarget, ConditionalBuilder/*!*/ bodyBuilder, bool reverse, DynamicMetaObject/*!*/[]/*!*/ types) {
  836. if (target == SlotOrFunction.Empty && slotTarget == null) return true;
  837. if (slotTarget != null) {
  838. MakeSlotCall(state, types, bodyBuilder, slotTarget, reverse);
  839. return true;
  840. } else if (target.MaybeNotImplemented) {
  841. Debug.Assert(target.ReturnType == typeof(object));
  842. ParameterExpression tmp = Ast.Variable(typeof(object), "slot");
  843. bodyBuilder.AddVariable(tmp);
  844. bodyBuilder.AddCondition(
  845. Ast.NotEqual(
  846. Ast.Assign(
  847. tmp,
  848. target.Target.Expression
  849. ),
  850. Ast.Property(null, typeof(PythonOps).GetProperty("NotImplemented"))
  851. ),
  852. tmp
  853. );
  854. return true;
  855. } else {
  856. bodyBuilder.FinishCondition(target.Target.Expression, typeof(object));
  857. return false;
  858. }
  859. }
  860. private static void MakeSlotCall(PythonContext/*!*/ state, DynamicMetaObject/*!*/[]/*!*/ types, ConditionalBuilder/*!*/ bodyBuilder, PythonTypeSlot/*!*/ slotTarget, bool reverse) {
  861. Debug.Assert(slotTarget != null);
  862. Expression self, other;
  863. if (reverse) {
  864. self = types[1].Expression;
  865. other = types[0].Expression;
  866. } else {
  867. self = types[0].Expression;
  868. other = types[1].Expression;
  869. }
  870. MakeSlotCallWorker(state, slotTarget, self, bodyBuilder, other);
  871. }
  872. private static void MakeSlotCallWorker(PythonContext/*!*/ state, PythonTypeSlot/*!*/ slotTarget, Expression/*!*/ self, ConditionalBuilder/*!*/ bodyBuilder, params Expression/*!*/[]/*!*/ args) {
  873. // Generate:
  874. //
  875. // SlotTryGetValue(context, slot, selfType, out callable) && (tmp=callable(args)) != NotImplemented) ?
  876. // tmp :
  877. // RestOfOperation
  878. //
  879. ParameterExpression callable = Ast.Variable(typeof(object), "slot");
  880. ParameterExpression tmp = Ast.Variable(typeof(object), "slot");
  881. bodyBuilder.AddCondition(
  882. Ast.AndAlso(
  883. Ast.Call(
  884. typeof(PythonOps).GetMethod("Slot…

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