/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
- /* ****************************************************************************
- *
- * Copyright (c) Microsoft Corporation.
- *
- * This source code is subject to terms and conditions of the Microsoft Public License. A
- * copy of the license can be found in the License.html file at the root of this distribution. If
- * you cannot locate the Microsoft Public License, please send an email to
- * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
- * by the terms of the Microsoft Public License.
- *
- * You must not remove this notice, or any other, from this software.
- *
- *
- * ***************************************************************************/
-
- using System; using Microsoft;
- using System.Collections;
- using System.Collections.Generic;
- using System.Diagnostics;
- using Microsoft.Linq.Expressions;
- using System.Reflection;
- using Microsoft.Scripting;
- using Microsoft.Scripting.Actions;
- using System.Text;
- using IronPython.Runtime.Operations;
- using IronPython.Runtime.Types;
- using Microsoft.Scripting.Actions.Calls;
- using Microsoft.Scripting.Ast;
- using Microsoft.Scripting.Generation;
- using Microsoft.Scripting.Math;
- using Microsoft.Scripting.Runtime;
- using Microsoft.Scripting.Utils;
-
- namespace IronPython.Runtime.Binding {
- using Ast = Microsoft.Linq.Expressions.Expression;
-
- static partial class PythonProtocol {
- private const string DisallowCoerce = "DisallowCoerce";
-
- public static MetaObject/*!*/ Operation(OperationAction/*!*/ operation, params MetaObject/*!*/[]/*!*/ args) {
- foreach (MetaObject mo in args) {
- if (mo.NeedsDeferral()) {
- return operation.Defer(args);
- }
- }
-
- ValidationInfo valInfo = BindingHelpers.GetValidationInfo(null, args);
-
- MetaObject res = MakeOperationRule(operation, args);
-
- return BindingHelpers.AddDynamicTestAndDefer(operation, res, args, valInfo);
- }
-
- private static MetaObject/*!*/ MakeOperationRule(OperationAction/*!*/ operation, MetaObject/*!*/[]/*!*/ args) {
- switch (operation.Operation) {
- case StandardOperators.Documentation:
- return MakeDocumentationOperation(operation, args);
- case StandardOperators.MemberNames:
- return MakeMemberNamesOperation(operation, args);
- case StandardOperators.CallSignatures:
- return MakeCallSignatureOperation(args[0], CompilerHelpers.GetMethodTargets(args[0].Value));
- case StandardOperators.IsCallable:
- return MakeIscallableOperation(operation, args);
-
- case StandardOperators.GetItem:
- case StandardOperators.SetItem:
- case StandardOperators.GetSlice:
- case StandardOperators.SetSlice:
- case StandardOperators.DeleteItem:
- case StandardOperators.DeleteSlice:
- // Indexers need to see if the index argument is an expandable tuple. This will
- // be captured in the AbstractValue in the future but today is captured in the
- // real value.
- return MakeIndexerOperation(operation, args);
-
- case StandardOperators.Not:
- return MakeUnaryNotOperation(operation, args[0]);
- case OperatorStrings.Hash:
- return MakeHashOperation(operation, args[0]);
-
- case StandardOperators.Contains:
- return MakeContainsOperation(operation, args);
-
- default:
- if (IsUnary(operation.Operation)) {
- return MakeUnaryOperation(operation, args[0]);
- } else if (IsComparision(operation.Operation)) {
- return MakeComparisonOperation(args, operation);
- }
-
- return MakeSimpleOperation(args, operation);
- }
- }
-
- #region Unary Operations
-
- /// <summary>
- /// Creates a rule for the contains operator. This is exposed via "x in y" in
- /// IronPython. It is implemented by calling the __contains__ method on x and
- /// passing in y.
- ///
- /// If a type doesn't define __contains__ but does define __getitem__ then __getitem__ is
- /// called repeatedly in order to see if the object is there.
- ///
- /// For normal .NET enumerables we'll walk the iterator and see if it's present.
- /// </summary>
- private static MetaObject/*!*/ MakeContainsOperation(OperationAction/*!*/ operation, MetaObject/*!*/[]/*!*/ types) {
- MetaObject res;
- // the paramteres come in backwards from how we look up __contains__, flip them.
- Debug.Assert(types.Length == 2);
- ArrayUtils.SwapLastTwo(types);
-
- BinderState state = BinderState.GetBinderState(operation);
- SlotOrFunction sf = SlotOrFunction.GetSlotOrFunction(state, Symbols.Contains, types);
-
- if (sf.Success) {
- // just a call to __contains__
- res = sf.Target;
- } else {
- RestrictTypes(types);
-
- sf = SlotOrFunction.GetSlotOrFunction(state, Symbols.Iterator, types[0]);
- if (sf.Success) {
- // iterate using __iter__
- res = new MetaObject(
- Ast.Call(
- typeof(PythonOps).GetMethod("ContainsFromEnumerable"),
- Ast.Constant(state.Context),
- Ast.Dynamic(
- new ConversionBinder(
- state,
- typeof(IEnumerator),
- ConversionResultKind.ExplicitCast
- ),
- typeof(IEnumerator),
- sf.Target.Expression
- ),
- Ast.ConvertHelper(types[1].Expression, typeof(object))
- ),
- Restrictions.Combine(types)
- );
- } else {
- ParameterExpression curIndex = Ast.Variable(typeof(int), "count");
- sf = SlotOrFunction.GetSlotOrFunction(state, Symbols.GetItem, types[0], new MetaObject(curIndex, Restrictions.Empty));
- if (sf.Success) {
- // defines __getitem__, need to loop over the indexes and see if we match
-
- ParameterExpression getItemRes = Ast.Variable(sf.ReturnType, "getItemRes");
- ParameterExpression containsRes = Ast.Variable(typeof(bool), "containsRes");
-
- LabelTarget target = Ast.Label();
- res = new MetaObject(
- Ast.Scope(
- Ast.Comma(
- Ast.Loop(
- null, // test
- Ast.Assign(curIndex, Ast.Add(curIndex, Ast.Constant(1))), // increment
- Ast.Block( // body
- // getItemRes = param0.__getitem__(curIndex)
- Utils.Try(
- Ast.Assign(
- getItemRes,
- sf.Target.Expression
- )
- ).Catch(
- // end of indexes, return false
- typeof(IndexOutOfRangeException),
- Ast.Break(target)
- ),
- // if(getItemRes == param1) return true
- Utils.If(
- Ast.Dynamic(
- new OperationBinder(
- state,
- StandardOperators.Equal
- ),
- typeof(bool),
- types[1].Expression,
- getItemRes
- ),
- Ast.Assign(containsRes, Ast.Constant(true)),
- Ast.Break(target)
- )
- ),
- null, // loop else
- target, // break label target
- null
- ),
- containsRes
- ),
- curIndex,
- getItemRes,
- containsRes
- ),
- Restrictions.Combine(types)
- );
- } else {
- // non-iterable object
- res = new MetaObject(
- Ast.Throw(
- Ast.Call(
- typeof(PythonOps).GetMethod("TypeErrorForNonIterableObject"),
- Ast.ConvertHelper(
- types[1].Expression,
- typeof(object)
- )
- )
- ),
- Restrictions.Combine(types)
- );
- }
- }
- }
-
- if (res.LimitType != typeof(bool) && res.LimitType != typeof(void)) {
- res = new MetaObject(
- Binders.Convert(
- state,
- typeof(bool),
- ConversionResultKind.ExplicitCast,
- res.Expression
- ),
- res.Restrictions
- );
- }
-
- return res;
- }
-
- private static void RestrictTypes(MetaObject/*!*/[] types) {
- for (int i = 0; i < types.Length; i++) {
- types[i] = types[i].Restrict(types[i].LimitType);
- }
- }
-
- private static MetaObject/*!*/ MakeHashOperation(OperationAction/*!*/ operation, MetaObject/*!*/ self) {
- self = self.Restrict(self.LimitType);
-
- BinderState state = BinderState.GetBinderState(operation);
- SlotOrFunction func = SlotOrFunction.GetSlotOrFunction(BinderState.GetBinderState(operation), Symbols.Hash, self);
- MetaObject res = func.Target;
-
- if (func.ReturnType != typeof(int)) {
- if (func.ReturnType == typeof(BigInteger)) {
- // Python 2.5 defines the result of returning a long as hashing the long
- res = new MetaObject(
- HashBigInt(operation, res.Expression),
- res.Restrictions
- );
- } else if (func.ReturnType == typeof(object)) {
- // need to get the integer value here...
- ParameterExpression tempVar = Ast.Parameter(typeof(object), "hashTemp");
-
- res = new MetaObject(
- Expression.Scope(
- Expression.Comma(
- Expression.Assign(tempVar, res.Expression),
- Expression.Condition(
- Expression.TypeIs(tempVar, typeof(int)),
- Expression.Convert(tempVar, typeof(int)),
- Expression.Condition(
- Expression.TypeIs(tempVar, typeof(BigInteger)),
- HashBigInt(operation, tempVar),
- HashConvertToInt(state, tempVar)
- )
- )
- ),
- new[] { tempVar }
- ),
- res.Restrictions
- );
- } else {
- // need to convert unknown value to object
- res = new MetaObject(
- HashConvertToInt(state, res.Expression),
- res.Restrictions
- );
- }
- }
-
- return res;
- }
-
- private static DynamicExpression/*!*/ HashBigInt(OperationAction/*!*/ operation, Expression/*!*/ expression) {
- return Ast.Dynamic(
- operation,
- typeof(int),
- expression
- );
- }
-
- private static DynamicExpression/*!*/ HashConvertToInt(BinderState/*!*/ state, Expression/*!*/ expression) {
- return Ast.Dynamic(
- new ConversionBinder(
- state,
- typeof(int),
- ConversionResultKind.ExplicitCast
- ),
- typeof(int),
- expression
- );
- }
-
-
- private static MetaObject/*!*/ MakeUnaryOperation(OperationAction/*!*/ operation, MetaObject/*!*/ self) {
- self = self.Restrict(self.LimitType);
-
- SlotOrFunction func = SlotOrFunction.GetSlotOrFunction(BinderState.GetBinderState(operation), Symbols.OperatorToSymbol(operation.Operation), self);
-
- if (!func.Success) {
- // we get the error message w/ {0} so that PythonBinderHelper.TypeError formats it correctly
- return TypeError(operation, MakeUnaryOpErrorMessage(operation.Operation.ToString(), "{0}"), self);
- }
-
- return func.Target;
- }
-
- private static MetaObject/*!*/ MakeUnaryNotOperation(OperationAction/*!*/ operation, MetaObject/*!*/ self) {
- self = self.Restrict(self.LimitType);
-
- SlotOrFunction nonzero = SlotOrFunction.GetSlotOrFunction(BinderState.GetBinderState(operation), Symbols.NonZero, self);
- SlotOrFunction length = SlotOrFunction.GetSlotOrFunction(BinderState.GetBinderState(operation), Symbols.Length, self);
-
- Expression notExpr;
-
- if (!nonzero.Success && !length.Success) {
- // always False or True for None
- notExpr = self.LimitType == typeof(None) ? Ast.True() : Ast.False();
- } else {
- SlotOrFunction target = nonzero.Success ? nonzero : length;
-
- notExpr = target.Target.Expression;
-
- if (nonzero.Success) {
- // call non-zero and negate it
- if (notExpr.Type == typeof(bool)) {
- notExpr = Ast.Equal(notExpr, Ast.False());
- } else {
- notExpr = Ast.Call(
- typeof(PythonOps).GetMethod("Not"),
- Ast.ConvertHelper(notExpr, typeof(object))
- );
- }
- } else {
- // call len, compare w/ zero
- if (notExpr.Type == typeof(int)) {
- notExpr = Ast.Equal(notExpr, Ast.Zero());
- } else {
- notExpr = Ast.Dynamic(
- new OperationBinder(
- BinderState.GetBinderState(operation),
- StandardOperators.Compare
- ),
- typeof(int),
- notExpr,
- Ast.Zero()
- );
- }
- }
- }
-
- return new MetaObject(
- notExpr,
- self.Restrictions.Merge(nonzero.Target.Restrictions.Merge(length.Target.Restrictions))
- );
- }
-
-
- #endregion
-
- #region Reflective Operations
-
- private static MetaObject/*!*/ MakeDocumentationOperation(OperationAction/*!*/ operation, MetaObject/*!*/[]/*!*/ args) {
- BinderState state = BinderState.GetBinderState(operation);
-
- return new MetaObject(
- Binders.Get(
- BinderState.GetCodeContext(operation),
- state,
- typeof(string),
- "__doc__",
- args[0].Expression
- ),
- args[0].Restrictions
- );
- }
-
- private static MetaObject/*!*/ MakeMemberNamesOperation(OperationAction/*!*/ operation, MetaObject[] args) {
- MetaObject self = args[0];
- CodeContext context;
- if (args.Length > 1 && args[0].LimitType == typeof(CodeContext)) {
- self = args[1];
- context = (CodeContext)args[0].Value;
- } else {
- context = BinderState.GetBinderState(operation).Context;
- }
-
- if (typeof(IMembersList).IsAssignableFrom(self.LimitType)) {
- return BinderState.GetBinderState(operation).Binder.DoOperation(operation.Operation, BinderState.GetCodeContext(operation), args);
- }
-
- PythonType pt = DynamicHelpers.GetPythonType(self.Value);
- List<string> strNames = GetMemberNames(context, pt, self.Value);
-
- if (pt.IsSystemType) {
- return new MetaObject(
- Ast.Constant(strNames),
- Restrictions.InstanceRestriction(self.Expression, self.Value).Merge(self.Restrictions)
- );
- }
-
- return new MetaObject(
- Ast.Constant(strNames),
- Restrictions.InstanceRestriction(self.Expression, self.Value).Merge(self.Restrictions)
- );
- }
-
- internal static MetaObject/*!*/ MakeCallSignatureOperation(MetaObject/*!*/ self, IList<MethodBase/*!*/>/*!*/ targets) {
- List<string> arrres = new List<string>();
- foreach (MethodBase mb in targets) {
- StringBuilder res = new StringBuilder();
- string comma = "";
-
- Type retType = CompilerHelpers.GetReturnType(mb);
- if (retType != typeof(void)) {
- res.Append(DynamicHelpers.GetPythonTypeFromType(retType).Name);
- res.Append(" ");
- }
-
- MethodInfo mi = mb as MethodInfo;
- if (mi != null) {
- string name;
- NameConverter.TryGetName(DynamicHelpers.GetPythonTypeFromType(mb.DeclaringType), mi, out name);
- res.Append(name);
- } else {
- res.Append(DynamicHelpers.GetPythonTypeFromType(mb.DeclaringType).Name);
- }
-
- res.Append("(");
- if (!CompilerHelpers.IsStatic(mb)) {
- res.Append("self");
- comma = ", ";
- }
-
- foreach (ParameterInfo pi in mb.GetParameters()) {
- if (pi.ParameterType == typeof(CodeContext)) continue;
-
- res.Append(comma);
- res.Append(DynamicHelpers.GetPythonTypeFromType(pi.ParameterType).Name + " " + pi.Name);
- comma = ", ";
- }
- res.Append(")");
- arrres.Add(res.ToString());
- }
-
- return new MetaObject(
- Ast.Constant(arrres.ToArray()),
- self.Restrictions.Merge(Restrictions.InstanceRestriction(self.Expression, self.Value))
- );
- }
-
- private static MetaObject/*!*/ MakeIscallableOperation(OperationAction/*!*/ operation, MetaObject/*!*/[]/*!*/ args) {
- // Certain non-python types (encountered during interop) are callable, but don't have
- // a __call__ attribute. The default base binder also checks these, but since we're overriding
- // the base binder, we check them here.
- MetaObject self = args[0];
-
- // only applies when called from a Python site
- if (typeof(Delegate).IsAssignableFrom(self.LimitType) ||
- typeof(MethodGroup).IsAssignableFrom(self.LimitType)) {
- return new MetaObject(
- Ast.Constant(true),
- self.Restrict(self.LimitType).Restrictions
- );
- }
-
- BinderState state = BinderState.GetBinderState(operation);
- Expression isCallable = Ast.NotEqual(
- Binders.TryGet(
- BinderState.GetCodeContext(operation),
- state,
- typeof(object),
- "__call__",
- self.Expression
- ),
- Ast.Constant(OperationFailed.Value)
- );
-
- return new MetaObject(
- isCallable,
- self.Restrict(self.LimitType).Restrictions
- );
- }
-
- #endregion
-
- #region Common Binary Operations
-
- private static MetaObject/*!*/ MakeSimpleOperation(MetaObject/*!*/[]/*!*/ types, OperationAction/*!*/ operation) {
- RestrictTypes(types);
-
- SlotOrFunction fbinder;
- SlotOrFunction rbinder;
- PythonTypeSlot fSlot;
- PythonTypeSlot rSlot;
- GetOpreatorMethods(types, operation.Operation, BinderState.GetBinderState(operation), out fbinder, out rbinder, out fSlot, out rSlot);
-
- return MakeBinaryOperatorResult(types, operation, fbinder, rbinder, fSlot, rSlot);
- }
-
- private static void GetOpreatorMethods(MetaObject/*!*/[]/*!*/ types, string oper, BinderState state, out SlotOrFunction fbinder, out SlotOrFunction rbinder, out PythonTypeSlot fSlot, out PythonTypeSlot rSlot) {
- oper = NormalizeOperator(oper);
- if (IsInPlace(oper)) {
- oper = DirectOperation(oper);
- }
-
- SymbolId op, rop;
- if (!TypeInfo.IsReverseOperator(oper)) {
- op = Symbols.OperatorToSymbol(oper);
- rop = Symbols.OperatorToReversedSymbol(oper);
- } else {
- // coming back after coercion, just try reverse operator.
- rop = Symbols.OperatorToSymbol(oper);
- op = Symbols.OperatorToReversedSymbol(oper);
- }
-
- fSlot = null;
- rSlot = null;
- PythonType fParent, rParent;
-
- if (oper == StandardOperators.Multiply &&
- IsSequence(types[0]) &&
- !PythonOps.IsNonExtensibleNumericType(types[1].LimitType)) {
- // class M:
- // def __rmul__(self, other):
- // print "CALLED"
- // return 1
- //
- // print [1,2] * M()
- //
- // in CPython this results in a successful call to __rmul__ on the type ignoring the forward
- // multiplication. But calling the __mul__ method directly does NOT return NotImplemented like
- // one might expect. Therefore we explicitly convert the MetaObject argument into an Index
- // for binding purposes. That allows this to work at multiplication time but not with
- // a direct call to __mul__.
-
- MetaObject[] newTypes = new MetaObject[2];
- newTypes[0] = types[0];
- newTypes[1] = new MetaObject(
- Ast.New(
- typeof(Index).GetConstructor(new Type[] { typeof(object) }),
- Ast.ConvertHelper(types[1].Expression, typeof(object))
- ),
- Restrictions.Empty
- );
- types = newTypes;
- }
-
- if (!SlotOrFunction.TryGetBinder(state, types, op, SymbolId.Empty, out fbinder, out fParent)) {
- foreach (PythonType pt in MetaPythonObject.GetPythonType(types[0]).ResolutionOrder) {
- if (pt.TryLookupSlot(state.Context, op, out fSlot)) {
- fParent = pt;
- break;
- }
- }
- }
-
- if (!SlotOrFunction.TryGetBinder(state, types, SymbolId.Empty, rop, out rbinder, out rParent)) {
- foreach (PythonType pt in MetaPythonObject.GetPythonType(types[1]).ResolutionOrder) {
- if (pt.TryLookupSlot(state.Context, rop, out rSlot)) {
- rParent = pt;
- break;
- }
- }
- }
-
- if (fParent != null && (rbinder.Success || rSlot != null) && rParent != fParent && rParent.IsSubclassOf(fParent)) {
- // Python says if x + subx and subx defines __r*__ we should call r*.
- fbinder = SlotOrFunction.Empty;
- fSlot = null;
- }
-
- if (!fbinder.Success && !rbinder.Success && fSlot == null && rSlot == null) {
- if (op == Symbols.OperatorTrueDivide || op == Symbols.OperatorReverseTrueDivide) {
- // true div on a type which doesn't support it, go ahead and try normal divide
- string newOp = op == Symbols.OperatorTrueDivide ? StandardOperators.Divide : OperatorStrings.ReverseDivide;
-
- GetOpreatorMethods(types, newOp, state, out fbinder, out rbinder, out fSlot, out rSlot);
- }
- }
- }
-
- private static bool IsSequence(MetaObject/*!*/ metaObject) {
- if (typeof(List).IsAssignableFrom(metaObject.LimitType) ||
- typeof(PythonTuple).IsAssignableFrom(metaObject.LimitType) ||
- typeof(String).IsAssignableFrom(metaObject.LimitType)) {
- return true;
- }
- return false;
- }
-
- private static MetaObject/*!*/ MakeBinaryOperatorResult(MetaObject/*!*/[]/*!*/ types, OperationAction/*!*/ operation, SlotOrFunction/*!*/ fCand, SlotOrFunction/*!*/ rCand, PythonTypeSlot fSlot, PythonTypeSlot rSlot) {
- Assert.NotNull(operation, fCand, rCand);
-
- string op = operation.Operation;
- SlotOrFunction fTarget, rTarget;
-
- // TODO: some Builder class for condition, body, vars
- ConditionalBuilder bodyBuilder = new ConditionalBuilder(operation);
-
- if (IsInPlace(op)) {
- // in place operator, see if there's a specific method that handles it.
- SlotOrFunction function = SlotOrFunction.GetSlotOrFunction(BinderState.GetBinderState(operation), Symbols.OperatorToSymbol(op), types);
-
- // we don't do a coerce for in place operators if the lhs implements __iop__
- if (!MakeOneCompareGeneric(function, false, types, MakeCompareReturn, bodyBuilder)) {
- // the method handles it and always returns a useful value.
- return bodyBuilder.GetMetaObject(types);
- }
- }
-
- if (!SlotOrFunction.GetCombinedTargets(fCand, rCand, out fTarget, out rTarget) &&
- fSlot == null &&
- rSlot == null &&
- !ShouldCoerce(operation, types[0], types[1], false) &&
- !ShouldCoerce(operation, types[1], types[0], false) &&
- bodyBuilder.NoConditions) {
- return MakeRuleForNoMatch(operation, op, types);
- }
-
- if (ShouldCoerce(operation, types[0], types[1], false) &&
- (op != StandardOperators.Mod || !MetaPythonObject.GetPythonType(types[0]).IsSubclassOf(TypeCache.String))) {
- // need to try __coerce__ first.
- DoCoerce(operation, bodyBuilder, op, types, false);
- }
-
- if (MakeOneTarget(BinderState.GetBinderState(operation), fTarget, fSlot, bodyBuilder, false, types)) {
- if (ShouldCoerce(operation, types[1], types[0], false)) {
- // need to try __coerce__ on the reverse first
- DoCoerce(operation, bodyBuilder, op, new MetaObject[] { types[1], types[0] }, true);
- }
-
- if (rSlot != null) {
- MakeSlotCall(BinderState.GetBinderState(operation), types, bodyBuilder, rSlot, true);
- bodyBuilder.FinishCondition(MakeBinaryThrow(operation, op, types).Expression);
- } else if (MakeOneTarget(BinderState.GetBinderState(operation), rTarget, rSlot, bodyBuilder, false, types)) {
- // need to fallback to throwing or coercion
- bodyBuilder.FinishCondition(MakeBinaryThrow(operation, op, types).Expression);
- }
- }
-
- return bodyBuilder.GetMetaObject(types);
- }
-
- private static void MakeCompareReturn(ConditionalBuilder/*!*/ bodyBuilder, Expression retCondition, Expression/*!*/ retValue, bool isReverse) {
- if (retCondition != null) {
- bodyBuilder.AddCondition(retCondition, retValue);
- } else {
- bodyBuilder.FinishCondition(retValue);
- }
- }
-
- /// <summary>
- /// Delegate for finishing the comparison. This takes in a condition and a return value and needs to update the ConditionalBuilder
- /// with the appropriate resulting body. The condition may be null.
- /// </summary>
- private delegate void ComparisonHelper(ConditionalBuilder/*!*/ bodyBuilder, Expression retCondition, Expression/*!*/ retValue, bool isReverse);
-
- /// <summary>
- /// Helper to handle a comparison operator call. Checks to see if the call can
- /// return NotImplemented and allows the caller to modify the expression that
- /// is ultimately returned (e.g. to turn __cmp__ into a bool after a comparison)
- /// </summary>
- private static bool MakeOneCompareGeneric(SlotOrFunction/*!*/ target, bool reverse, MetaObject/*!*/[]/*!*/ types, ComparisonHelper returner, ConditionalBuilder/*!*/ bodyBuilder) {
- if (target == SlotOrFunction.Empty || !target.Success) return true;
-
- ParameterExpression tmp;
-
- if (target.ReturnType == typeof(bool)) {
- tmp = bodyBuilder.CompareRetBool;
- } else {
- tmp = Ast.Variable(target.ReturnType, "compareRetValue");
- bodyBuilder.AddVariable(tmp);
- }
-
- if (target.MaybeNotImplemented) {
- Expression call = target.Target.Expression;
- Expression assign = Ast.Assign(tmp, call);
-
- returner(
- bodyBuilder,
- Ast.NotEqual(
- assign,
- Ast.Constant(PythonOps.NotImplemented)
- ),
- tmp,
- reverse);
- return true;
- } else {
- returner(
- bodyBuilder,
- null,
- target.Target.Expression,
- reverse
- );
- return false;
- }
- }
-
- private static bool MakeOneTarget(BinderState/*!*/ state, SlotOrFunction/*!*/ target, PythonTypeSlot slotTarget, ConditionalBuilder/*!*/ bodyBuilder, bool reverse, MetaObject/*!*/[]/*!*/ types) {
- if (target == SlotOrFunction.Empty && slotTarget == null) return true;
-
- if (slotTarget != null) {
- MakeSlotCall(state, types, bodyBuilder, slotTarget, reverse);
- return true;
- } else if (target.MaybeNotImplemented) {
- Debug.Assert(target.ReturnType == typeof(object));
-
- ParameterExpression tmp = Ast.Variable(typeof(object), "slot");
- bodyBuilder.AddVariable(tmp);
-
- bodyBuilder.AddCondition(
- Ast.NotEqual(
- Ast.Assign(
- tmp,
- target.Target.Expression
- ),
- Ast.Property(null, typeof(PythonOps).GetProperty("NotImplemented"))
- ),
- tmp
- );
-
- return true;
- } else {
- bodyBuilder.FinishCondition(target.Target.Expression);
- return false;
- }
- }
-
- private static void MakeSlotCall(BinderState/*!*/ state, MetaObject/*!*/[]/*!*/ types, ConditionalBuilder/*!*/ bodyBuilder, PythonTypeSlot/*!*/ slotTarget, bool reverse) {
- Debug.Assert(slotTarget != null);
-
- Expression self, other;
- if (reverse) {
- self = types[1].Expression;
- other = types[0].Expression;
- } else {
- self = types[0].Expression;
- other = types[1].Expression;
- }
-
- MakeSlotCallWorker(state, slotTarget, self, bodyBuilder, other);
- }
-
- private static void MakeSlotCallWorker(BinderState/*!*/ state, PythonTypeSlot/*!*/ slotTarget, Expression/*!*/ self, ConditionalBuilder/*!*/ bodyBuilder, params Expression/*!*/[]/*!*/ args) {
- // Generate:
- //
- // SlotTryGetValue(context, slot, selfType, out callable) && (tmp=callable(args)) != NotImplemented) ?
- // tmp :
- // RestOfOperation
- //
- ParameterExpression callable = Ast.Variable(typeof(object), "slot");
- ParameterExpression tmp = Ast.Variable(typeof(object), "slot");
-
- bodyBuilder.AddCondition(
- Ast.AndAlso(
- Ast.Call(
- typeof(PythonOps).GetMethod("SlotTryGetValue"),
- Ast.Constant(state.Context),
- Ast.ConvertHelper(Utils.WeakConstant(slotTarget), typeof(PythonTypeSlot)),
- Ast.ConvertHelper(self, typeof(object)),
- Ast.Call(
- typeof(DynamicHelpers).GetMethod("GetPythonType"),
- Ast.ConvertHelper(self, typeof(object))
- ),
- callable
- ),
- Ast.NotEqual(
- Ast.Assign(
- tmp,
- Ast.Dynamic(
- new InvokeBinder(
- state,
- new CallSignature(args.Length)
- ),
- typeof(object),
- ArrayUtils.Insert(Ast.Constant(state.Context), (Expression)callable, args)
- )
- ),
- Ast.Property(null, typeof(PythonOps).GetProperty("NotImplemented"))
- )
- ),
- tmp
- );
- bodyBuilder.AddVariable(callable);
- bodyBuilder.AddVariable(tmp);
- }
-
- private static void DoCoerce(OperationAction/*!*/ operation, ConditionalBuilder/*!*/ bodyBuilder, string op, MetaObject/*!*/[]/*!*/ types, bool reverse) {
- DoCoerce(operation, bodyBuilder, op, types, reverse, delegate(Expression e) {
- return e;
- });
- }
-
- /// <summary>
- /// calls __coerce__ for old-style classes and performs the operation if the coercion is successful.
- /// </summary>
- private static void DoCoerce(OperationAction/*!*/ operation, ConditionalBuilder/*!*/ bodyBuilder, string op, MetaObject/*!*/[]/*!*/ types, bool reverse, Func<Expression, Expression> returnTransform) {
- ParameterExpression coerceResult = Ast.Variable(typeof(object), "coerceResult");
- ParameterExpression coerceTuple = Ast.Variable(typeof(PythonTuple), "coerceTuple");
-
- if (!bodyBuilder.TestCoercionRecursionCheck) {
- // during coercion we need to enforce recursion limits if
- // they're enabled and the rule's test needs to reflect this.
- bodyBuilder.Restrictions = bodyBuilder.Restrictions.Merge(
- Restrictions.ExpressionRestriction(
- Ast.Equal(
- Ast.Call(typeof(PythonOps).GetMethod("ShouldEnforceRecursion")),
- Ast.Constant(PythonFunction.EnforceRecursion)
- )
- )
- );
-
- bodyBuilder.TestCoercionRecursionCheck = true;
- }
-
- // tmp = self.__coerce__(other)
- // if tmp != null && tmp != NotImplemented && (tuple = PythonOps.ValidateCoerceResult(tmp)) != null:
- // return operation(tuple[0], tuple[1])
- SlotOrFunction slot = SlotOrFunction.GetSlotOrFunction(BinderState.GetBinderState(operation), Symbols.Coerce, types);
-
- if (slot.Success) {
- bodyBuilder.AddCondition(
- Ast.AndAlso(
- Ast.Not(
- Ast.TypeIs(
- Ast.Assign(
- coerceResult,
- slot.Target.Expression
- ),
- typeof(OldInstance)
- )
- ),
- Ast.NotEqual(
- Ast.Assign(
- coerceTuple,
- Ast.Call(
- typeof(PythonOps).GetMethod("ValidateCoerceResult"),
- coerceResult
- )
- ),
- Ast.Constant(null)
- )
- ),
- BindingHelpers.AddRecursionCheck(
- returnTransform(
- Ast.Dynamic(
- new OperationBinder(
- BinderState.GetBinderState(operation),
- DisallowCoerce + op
- ),
- typeof(object),
- reverse ? CoerceTwo(coerceTuple) : CoerceOne(coerceTuple),
- reverse ? CoerceOne(coerceTuple) : CoerceTwo(coerceTuple)
- )
- )
- )
- );
- bodyBuilder.AddVariable(coerceResult);
- bodyBuilder.AddVariable(coerceTuple);
- }
- }
-
- private static MethodCallExpression/*!*/ CoerceTwo(ParameterExpression/*!*/ coerceTuple) {
- return Ast.Call(
- typeof(PythonOps).GetMethod("GetCoerceResultTwo"),
- coerceTuple
- );
- }
-
- private static MethodCallExpression/*!*/ CoerceOne(ParameterExpression/*!*/ coerceTuple) {
- return Ast.Call(
- typeof(PythonOps).GetMethod("GetCoerceResultOne"),
- coerceTuple
- );
- }
-
-
- #endregion
-
- #region Comparison Operations
-
- private static MetaObject/*!*/ MakeComparisonOperation(MetaObject/*!*/[]/*!*/ types, OperationAction/*!*/ operation) {
- RestrictTypes(types);
-
- string op = NormalizeOperator(operation.Operation);
- if (op == StandardOperators.Compare) {
- return MakeSortComparisonRule(types, operation);
- }
-
- BinderState state = BinderState.GetBinderState(operation);
- Debug.Assert(types.Length == 2);
- MetaObject xType = types[0], yType = types[1];
- SymbolId opSym = Symbols.OperatorToSymbol(op);
- SymbolId ropSym = Symbols.OperatorToReversedSymbol(op);
- // reverse
- MetaObject[] rTypes = new MetaObject[] { types[1], types[0] };
-
- SlotOrFunction fop, rop, cmp, rcmp;
- fop = SlotOrFunction.GetSlotOrFunction(state, opSym, types);
- rop = SlotOrFunction.GetSlotOrFunction(state, ropSym, rTypes);
- cmp = SlotOrFunction.GetSlotOrFunction(state, Symbols.Cmp, types);
- rcmp = SlotOrFunction.GetSlotOrFunction(state, Symbols.Cmp, rTypes);
-
- ConditionalBuilder bodyBuilder = new ConditionalBuilder(operation);
-
- SlotOrFunction.GetCombinedTargets(fop, rop, out fop, out rop);
- SlotOrFunction.GetCombinedTargets(cmp, rcmp, out cmp, out rcmp);
-
- // first try __op__ or __rop__ and return the value
- if (MakeOneCompareGeneric(fop, false, types, MakeCompareReturn, bodyBuilder)) {
- if (MakeOneCompareGeneric(rop, true, types, MakeCompareReturn, bodyBuilder)) {
-
- // then try __cmp__ or __rcmp__ and compare the resulting int appropriaetly
- if (ShouldCoerce(operation, xType, yType, true)) {
- DoCoerce(operation, bodyBuilder, StandardOperators.Compare, types, false, delegate(Expression e) {
- return GetCompareTest(op, e, false);
- });
- }
-
- if (MakeOneCompareGeneric(
- cmp,
- false,
- types,
- delegate(ConditionalBuilder builder, Expression retCond, Expression expr, bool reverse) {
- MakeCompareTest(op, builder, retCond, expr, reverse);
- },
- bodyBuilder)) {
-
- if (ShouldCoerce(operation, yType, xType, true)) {
- DoCoerce(operation, bodyBuilder, StandardOperators.Compare, rTypes, true, delegate(Expression e) {
- return GetCompareTest(op, e, true);
- });
- }
-
- if (MakeOneCompareGeneric(
- rcmp,
- true,
- types,
- delegate(ConditionalBuilder builder, Expression retCond, Expression expr, bool reverse) {
- MakeCompareTest(op, builder, retCond, expr, reverse);
- },
- bodyBuilder)) {
- bodyBuilder.FinishCondition(MakeFallbackCompare(op, types));
- }
- }
- }
- }
-
- return bodyBuilder.GetMetaObject(types);
- }
-
- /// <summary>
- /// Makes the comparison rule which returns an int (-1, 0, 1). TODO: Better name?
- /// </summary>
- private static MetaObject/*!*/ MakeSortComparisonRule(MetaObject/*!*/[]/*!*/ types, OperationAction/*!*/ operation) {
- MetaObject fastPath = FastPathCompare(types);
- if (fastPath != null) {
- return fastPath;
- }
-
- string op = operation.Operation;
-
- // Python compare semantics:
- // if the types are the same invoke __cmp__ first.
- // If __cmp__ is not defined or the types are different:
- // try rich comparisons (eq, lt, gt, etc...)
- // If the types are not the same and rich cmp didn't work finally try __cmp__
- // If __cmp__ isn't defined return a comparison based upon the types.
- //
- // Along the way we try both forward and reverse versions (try types[0] and then
- // try types[1] reverse version). For these comparisons __cmp__ and __eq__ are their
- // own reversals and __gt__ is the opposite of __lt__.
-
- // collect all the comparison methods, most likely we won't need them all.
- MetaObject[] rTypes = new MetaObject[] { types[1], types[0] };
- SlotOrFunction cfunc, rcfunc, eqfunc, reqfunc, ltfunc, gtfunc, rltfunc, rgtfunc;
-
- BinderState state = BinderState.GetBinderState(operation);
- cfunc = SlotOrFunction.GetSlotOrFunction(state, Symbols.Cmp, types);
- rcfunc = SlotOrFunction.GetSlotOrFunction(state, Symbols.Cmp, rTypes);
- eqfunc = SlotOrFunction.GetSlotOrFunction(state, Symbols.OperatorEquals, types);
- reqfunc = SlotOrFunction.GetSlotOrFunction(state, Symbols.OperatorEquals, rTypes);
- ltfunc = SlotOrFunction.GetSlotOrFunction(state, Symbols.OperatorLessThan, types);
- gtfunc = SlotOrFunction.GetSlotOrFunction(state, Symbols.OperatorGreaterThan, types);
- rltfunc = SlotOrFunction.GetSlotOrFunction(state, Symbols.OperatorLessThan, rTypes);
- rgtfunc = SlotOrFunction.GetSlotOrFunction(state, Symbols.OperatorGreaterThan, rTypes);
-
- // inspect forward and reverse versions so we can pick one or both.
- SlotOrFunction cTarget, rcTarget, eqTarget, reqTarget, ltTarget, rgtTarget, gtTarget, rltTarget;
- SlotOrFunction.GetCombinedTargets(cfunc, rcfunc, out cTarget, out rcTarget);
- SlotOrFunction.GetCombinedTargets(eqfunc, reqfunc, out eqTarget, out reqTarget);
- SlotOrFunction.GetCombinedTargets(ltfunc, rgtfunc, out ltTarget, out rgtTarget);
- SlotOrFunction.GetCombinedTargets(gtfunc, rltfunc, out gtTarget, out rltTarget);
-
- PythonType xType = MetaPythonObject.GetPythonType(types[0]);
- PythonType yType = MetaPythonObject.GetPythonType(types[1]);
-
- // now build the rule from the targets.
- // bail if we're comparing to null and the rhs can't do anything special...
- if (xType.IsNull) {
- if (yType.IsNull) {
- return new MetaObject(
- Ast.Zero(),
- Restrictions.Combine(types)
- );
- } else if (yType.UnderlyingSystemType.IsPrimitive || yType.UnderlyingSystemType == typeof(Microsoft.Scripting.Math.BigInteger)) {
- return new MetaObject(
- Ast.Constant(-1),
- Restrictions.Combine(types)
- );
- }
- }
-
- ConditionalBuilder bodyBuilder = new ConditionalBuilder(operation);
-
- bool tryRich = true, more = true;
- if (xType == yType && cTarget != SlotOrFunction.Empty) {
- // if the types are equal try __cmp__ first
- if (ShouldCoerce(operation, types[0], types[1], true)) {
- // need to try __coerce__ first.
- DoCoerce(operation, bodyBuilder, StandardOperators.Compare, types, false);
- }
-
- more = more && MakeOneCompareGeneric(cTarget, false, types, MakeCompareReverse, bodyBuilder);
-
- if (xType != TypeCache.OldInstance) {
- // try __cmp__ backwards for new-style classes and don't fallback to
- // rich comparisons if available
- more = more && MakeOneCompareGeneric(rcTarget, true, types, MakeCompareReverse, bodyBuilder);
- tryRich = false;
- }
- }
-
- if (tryRich && more) {
- // try the >, <, ==, !=, >=, <=. These don't get short circuited using the more logic
- // because they don't give a definitive answer even if they return bool. Only if they
- // return true do we know to return 0, -1, or 1.
- // try eq
- MakeOneCompareGeneric(eqTarget, false, types, MakeCompareToZero, bodyBuilder);
- MakeOneCompareGeneric(reqTarget, true, types, MakeCompareToZero, bodyBuilder);
-
- // try less than & reveā¦
Large files files are truncated, but you can click here to view the full file