Inheritance: System.Dynamic.DynamicMetaObjectBinder
        public override DynamicMetaObject BindOperation(OperationBinder action, DynamicMetaObject[] args) {
            switch (action.Operation) {
                case StandardOperators.CallSignatures:
                    return PythonProtocol.MakeCallSignatureOperation(this, Value.Template.Targets);
            }

            return base.BindOperation(action, args);
        }
Esempio n. 2
0
        public override DynamicMetaObject/*!*/ BindOperation(OperationBinder/*!*/ action, DynamicMetaObject/*!*/[]/*!*/ args) {
            switch (action.Operation) {
                case StandardOperators.CallSignatures:
                    return MakeCallSignatureRule(this);
                case StandardOperators.IsCallable:
                    return MakeIsCallableRule(this);
            }

            return base.BindOperation(action, args);
        }
Esempio n. 3
0
        public override DynamicMetaObject/*!*/ BindOperation(OperationBinder operation, params DynamicMetaObject/*!*/[]/*!*/ args) {
            if (operation.Operation == StandardOperators.IsCallable) {
                return new DynamicMetaObject(
                    Ast.Constant(true),
                    Restrictions.Merge(BindingRestrictionsHelpers.GetRuntimeTypeRestriction(Expression, typeof(OldClass)))
                );
            }

            return base.BindOperation(operation, args);
        }
        public static DynamicMetaObject/*!*/ Operation(OperationBinder/*!*/ operation, params DynamicMetaObject/*!*/[]/*!*/ args) {
            foreach (DynamicMetaObject mo in args) {
                if (mo.NeedsDeferral()) {
                    return operation.Defer(args);
                }
            }

            ValidationInfo valInfo = BindingHelpers.GetValidationInfo(null, args);

            DynamicMetaObject res = MakeOperationRule(operation, args);

            if (res.Expression.Type.IsValueType) {
                // Use Python boxing rules if we're return a value type
                res = new DynamicMetaObject(
                    AstUtils.Convert(res.Expression, typeof(object)),
                    res.Restrictions
                );
            }
            return BindingHelpers.AddDynamicTestAndDefer(operation, res, args, valInfo);
        }
        private static DynamicMetaObject/*!*/ MakeOperationRule(OperationBinder/*!*/ operation, DynamicMetaObject/*!*/[]/*!*/ 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);
            }
        }
        private static DynamicMetaObject/*!*/ MakeBinaryOperatorResult(DynamicMetaObject/*!*/[]/*!*/ types, OperationBinder/*!*/ 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 DynamicMetaObject[] { 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 DynamicMetaObject/*!*/ MakeSimpleOperation(DynamicMetaObject/*!*/[]/*!*/ types, OperationBinder/*!*/ 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 DynamicMetaObject/*!*/ MakeIscallableOperation(OperationBinder/*!*/ operation, DynamicMetaObject/*!*/[]/*!*/ 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.
            DynamicMetaObject self = args[0];
            
            // only applies when called from a Python site
            if (typeof(Delegate).IsAssignableFrom(self.GetLimitType()) ||
                typeof(MethodGroup).IsAssignableFrom(self.GetLimitType())) {
                return new DynamicMetaObject(
                    Ast.Constant(true),
                    self.Restrict(self.GetLimitType()).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 DynamicMetaObject(
                isCallable,
                self.Restrict(self.GetLimitType()).Restrictions
            );
        }
        /// <summary>
        /// Python has three protocols for slicing:
        ///    Simple Slicing x[i:j]
        ///    Extended slicing x[i,j,k,...]
        ///    Long Slice x[start:stop:step]
        /// 
        /// The first maps to __*slice__ (get, set, and del).  
        ///    This takes indexes - i, j - which specify the range of elements to be
        ///    returned.  In the slice variants both i, j must be numeric data types.  
        /// The 2nd and 3rd are both __*item__.  
        ///    This receives a single index which is either a Tuple or a Slice object (which 
        ///    encapsulates the start, stop, and step values) 
        /// 
        /// This is in addition to a simple indexing x[y].
        /// 
        /// For simple slicing and long slicing Python generates Operators.*Slice.  For
        /// the extended slicing and simple indexing Python generates a Operators.*Item
        /// action.
        /// 
        /// Extended slicing maps to the normal .NET multi-parameter input.  
        /// 
        /// So our job here is to first determine if we're to call a __*slice__ method or
        /// a __*item__ method.  
        private static DynamicMetaObject/*!*/ MakeIndexerOperation(OperationBinder/*!*/ operation, DynamicMetaObject/*!*/[]/*!*/ types) {
            SymbolId item, slice;
            DynamicMetaObject indexedType = types[0].Restrict(types[0].GetLimitType());
            BinderState state = BinderState.GetBinderState(operation);
            BuiltinFunction itemFunc = null;
            PythonTypeSlot itemSlot = null;
            bool callSlice = false;
            int mandatoryArgs;
            string op = operation.Operation;

            GetIndexOperators(op, out item, out slice, out mandatoryArgs);

            if (types.Length == mandatoryArgs + 1 && IsSlice(op) && HasOnlyNumericTypes(operation, types, op == StandardOperators.SetSlice)) {
                // two slice indexes, all int arguments, need to call __*slice__ if it exists
                callSlice = BindingHelpers.TryGetStaticFunction(state, slice, indexedType, out itemFunc);
                if (itemFunc == null || !callSlice) {
                    callSlice = MetaPythonObject.GetPythonType(indexedType).TryResolveSlot(state.Context, slice, out itemSlot);
                }
            }

            if (!callSlice) {
                // 1 slice index (simple index) or multiple slice indexes or no __*slice__, call __*item__, 
                if (!BindingHelpers.TryGetStaticFunction(state, item, indexedType, out itemFunc)) {
                    MetaPythonObject.GetPythonType(indexedType).TryResolveSlot(state.Context, item, out itemSlot);
                }
            }

            // make the Callable object which does the actual call to the function or slot
            Callable callable = Callable.MakeCallable(state, op, itemFunc, itemSlot);
            if (callable == null) {
                return TypeError(operation, "'{0}' object is unsubscriptable", indexedType);
            }

            // prepare the arguments and make the builder which will
            // call __*slice__ or __*item__
            DynamicMetaObject[] args;
            IndexBuilder builder;
            if (callSlice) {
                // we're going to call a __*slice__ method, we pass the args as is.
                Debug.Assert(IsSlice(op));

                builder = new SliceBuilder(types, callable);
                args = ConvertArgs(types);
            } else {
                // we're going to call a __*item__ method.
                builder = new ItemBuilder(types, callable);
                if (IsSlice(op)) {
                    // we need to create a new Slice object.
                    args = GetItemSliceArguments(state, op, types);
                } else {
                    // we just need to pass the arguments as they are
                    args = ConvertArgs(types);
                }
            }

            return builder.MakeRule(state, args);
        }
Esempio n. 10
0
 public virtual DynamicMetaObject BindOperation(OperationBinder binder, params DynamicMetaObject[] args)
 {
     ContractUtils.RequiresNotNull(binder, "binder");
     return binder.FallbackOperation(this, args);
 }
        /// <summary>
        /// Makes the comparison rule which returns an int (-1, 0, 1).  TODO: Better name?
        /// </summary>
        private static DynamicMetaObject/*!*/ MakeSortComparisonRule(DynamicMetaObject/*!*/[]/*!*/ types, OperationBinder/*!*/ operation) {
            DynamicMetaObject 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.
            DynamicMetaObject[] rTypes = new DynamicMetaObject[] { 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 DynamicMetaObject(
                        Ast.Constant(0),
                        BindingRestrictions.Combine(types)
                    );
                } else if (yType.UnderlyingSystemType.IsPrimitive || yType.UnderlyingSystemType == typeof(Microsoft.Scripting.Math.BigInteger)) {
                    return new DynamicMetaObject(
                        Ast.Constant(-1),
                        BindingRestrictions.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 & reverse
                MakeOneCompareGeneric(ltTarget, false, types, MakeCompareToNegativeOne, bodyBuilder);
                MakeOneCompareGeneric(rgtTarget, true, types, MakeCompareToNegativeOne, bodyBuilder);

                // try greater than & reverse
                MakeOneCompareGeneric(gtTarget, false, types, MakeCompareToOne, bodyBuilder);
                MakeOneCompareGeneric(rltTarget, true, types, MakeCompareToOne, bodyBuilder);
            }

            if (xType != yType) {
                if (more && 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 (more && ShouldCoerce(operation, types[1], types[0], true)) {
                    // try __coerce__ first
                    DoCoerce(operation, bodyBuilder, StandardOperators.Compare, rTypes, true, delegate(Expression e) {
                        return ReverseCompareValue(e);
                    });
                }

                more = more && MakeOneCompareGeneric(rcTarget, true, types, MakeCompareReverse, bodyBuilder);
            }

            if (more) {
                // fall back to compare types
                bodyBuilder.FinishCondition(MakeFallbackCompare(op, types));
            }

            return bodyBuilder.GetMetaObject(types);
        }
        /// <summary>
        /// calls __coerce__ for old-style classes and performs the operation if the coercion is successful.
        /// </summary>
        private static void DoCoerce(OperationBinder/*!*/ operation, ConditionalBuilder/*!*/ bodyBuilder, string op, DynamicMetaObject/*!*/[]/*!*/ 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(
                    BindingRestrictions.GetExpressionRestriction(
                        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 PythonOperationBinder(
                                    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 DynamicMetaObject/*!*/ MakeUnaryOperation(OperationBinder/*!*/ operation, DynamicMetaObject/*!*/ self) {
            self = self.Restrict(self.GetLimitType());

            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 DynamicExpression/*!*/ HashBigInt(OperationBinder/*!*/ operation, Expression/*!*/ expression) {
     return Ast.Dynamic(
         operation,
         typeof(int),
         expression
     );
 }
        private static DynamicMetaObject/*!*/ MakeHashOperation(OperationBinder/*!*/ operation, DynamicMetaObject/*!*/ self) {
            self = self.Restrict(self.GetLimitType());

            BinderState state = BinderState.GetBinderState(operation);
            SlotOrFunction func = SlotOrFunction.GetSlotOrFunction(state, Symbols.Hash, self);
            DynamicMetaObject 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 DynamicMetaObject(
                        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 DynamicMetaObject(
                            Expression.Block(
                                new [] { tempVar },
                                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)
                                    )
                                )
                            ),
                            res.Restrictions
                        );
                } else {
                    // need to convert unknown value to object
                    res = new DynamicMetaObject(
                        HashConvertToInt(state, res.Expression),
                        res.Restrictions
                    );
                }
            }

            return res;
        }
        /// <summary>
        /// Produces an error message for the provided message and type names.  The error message should contain
        /// string formatting characters ({0}, {1}, etc...) for each of the type names.
        /// </summary>
        public static DynamicMetaObject/*!*/ TypeError(OperationBinder/*!*/ action, string message, params DynamicMetaObject[] types) {
            if (action is IPythonSite) {
                // produce our custom errors for Python...
                Expression[] formatArgs = new Expression[types.Length + 1];
                for (int i = 1; i < formatArgs.Length; i++) {
                    formatArgs[i] = Ast.Constant(MetaPythonObject.GetPythonType(types[i - 1]).Name);
                }
                formatArgs[0] = Ast.Constant(message);
                Type[] typeArgs = CompilerHelpers.MakeRepeatedArray<Type>(typeof(object), types.Length + 1);
                typeArgs[0] = typeof(string);

                Expression error = Ast.Throw(
                    Ast.Call(
                        typeof(ScriptingRuntimeHelpers).GetMethod("SimpleTypeError"),
                        AstUtils.ComplexCallHelper(
                            typeof(String).GetMethod("Format", typeArgs),
                            formatArgs
                        )
                    )
                );

                return new DynamicMetaObject(
                    error,
                    BindingRestrictions.Combine(types)
                );
            }

            return action.FallbackOperation(types[0], ArrayUtils.RemoveFirst(types));
        }
        private static DynamicMetaObject/*!*/ MakeBinaryThrow(OperationBinder/*!*/action, string/*!*/ op, DynamicMetaObject/*!*/[]/*!*/ args) {
            if (action is IPythonSite) {
                // produce the custom Python error message
                return new DynamicMetaObject(
                    Ast.Throw(
                        Ast.Call(
                            typeof(PythonOps).GetMethod("TypeErrorForBinaryOp"),
                            Ast.Constant(SymbolTable.IdToString(Symbols.OperatorToSymbol(NormalizeOperator(op)))),
                            AstUtils.Convert(args[0].Expression, typeof(object)),
                            AstUtils.Convert(args[1].Expression, typeof(object))
                        )
                    ),
                    BindingRestrictions.Combine(args)
                );
            }

            // let the site produce its own error
            return action.FallbackOperation(args[0], new[] { args[1] });
        }
 private static DynamicMetaObject/*!*/ MakeRuleForNoMatch(OperationBinder/*!*/ operation, string/*!*/ op, params DynamicMetaObject/*!*/[]/*!*/ types) {
     // we get the error message w/ {0}, {1} so that TypeError formats it correctly
     return TypeError(
            operation,
            MakeBinaryOpErrorMessage(op, "{0}", "{1}"),
            types);
 }
        /// <summary>
        /// Checks if a coercion check should be performed.  We perform coercion under the following
        /// situations:
        ///     1. Old instances performing a binary operator (excluding rich comparisons)
        ///     2. User-defined new instances calling __cmp__ but only if we wouldn't dispatch to a built-in __coerce__ on the parent type
        ///     
        /// This matches the behavior of CPython.
        /// </summary>
        /// <returns></returns>
        private static bool ShouldCoerce(OperationBinder/*!*/ operation, DynamicMetaObject/*!*/ x, DynamicMetaObject/*!*/ y, bool isCompare) {
            if (operation.Operation.StartsWith(DisallowCoerce)) {
                return false;
            }

            PythonType xType = MetaPythonObject.GetPythonType(x), yType = MetaPythonObject.GetPythonType(y);

            if (xType == TypeCache.OldInstance) return true;

            if (isCompare && !xType.IsSystemType && yType.IsSystemType) {
                if (yType == TypeCache.Int32 ||
                    yType == TypeCache.BigInteger ||
                    yType == TypeCache.Double ||
                    yType == TypeCache.Complex64) {

                    // only coerce new style types that define __coerce__ and
                    // only when comparing against built-in types which
                    // define __coerce__
                    PythonTypeSlot pts;
                    if (xType.TryResolveSlot(BinderState.GetBinderState(operation).Context, Symbols.Coerce, out pts)) {
                        // don't call __coerce__ if it's declared on the base type
                        BuiltinMethodDescriptor bmd = pts as BuiltinMethodDescriptor;
                        if (bmd == null) return true;

                        if (bmd.__name__ != "__coerce__" &&
                            bmd.DeclaringType != typeof(int) &&
                            bmd.DeclaringType != typeof(BigInteger) &&
                            bmd.DeclaringType != typeof(double) &&
                            bmd.DeclaringType != typeof(Complex64)) {
                            return true;
                        }

                        foreach (PythonType pt in xType.ResolutionOrder) {
                            if (pt.UnderlyingSystemType == bmd.DeclaringType) {
                                // inherited __coerce__
                                return false;
                            }
                        }

                        return true;
                    }
                }
            }

            return false;
        }
 private static void DoCoerce(OperationBinder/*!*/ operation, ConditionalBuilder/*!*/ bodyBuilder, string op, DynamicMetaObject/*!*/[]/*!*/ types, bool reverse) {
     DoCoerce(operation, bodyBuilder, op, types, reverse, delegate(Expression e) {
         return e;
     });
 }
        private static DynamicMetaObject/*!*/ MakeUnaryNotOperation(OperationBinder/*!*/ operation, DynamicMetaObject/*!*/ self) {
            self = self.Restrict(self.GetLimitType());

            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.GetLimitType() == typeof(DynamicNull)) ? Ast.Constant(true) : Ast.Constant(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.Constant(false));
                    } else {
                        notExpr = Ast.Call(
                            typeof(PythonOps).GetMethod("Not"),
                            AstUtils.Convert(notExpr, typeof(object))
                        );
                    }
                } else {
                    // call len, compare w/ zero
                    if (notExpr.Type == typeof(int)) {
                        notExpr = Ast.Equal(notExpr, Ast.Constant(0));
                    } else {
                        notExpr = Ast.Dynamic(
                            new PythonOperationBinder(
                                BinderState.GetBinderState(operation),
                                StandardOperators.Compare
                            ),
                            typeof(int),
                            notExpr,
                            Ast.Constant(0)
                        );
                    }
                }
            }

            return new DynamicMetaObject(
                notExpr,
                self.Restrictions.Merge(nonzero.Target.Restrictions.Merge(length.Target.Restrictions))
            );
        }
        private static DynamicMetaObject/*!*/ MakeComparisonOperation(DynamicMetaObject/*!*/[]/*!*/ types, OperationBinder/*!*/ 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);
            DynamicMetaObject xType = types[0], yType = types[1];
            SymbolId opSym = Symbols.OperatorToSymbol(op);
            SymbolId ropSym = Symbols.OperatorToReversedSymbol(op);
            // reverse
            DynamicMetaObject[] rTypes = new DynamicMetaObject[] { 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);
        }
Esempio n. 23
0
        public override bool Equals(object obj)
        {
            OperationBinder oa = obj as OperationBinder;

            return(oa != null && oa._operation == _operation);
        }
Esempio n. 24
0
 public override DynamicMetaObject/*!*/ BindOperation(OperationBinder/*!*/ operation, params DynamicMetaObject/*!*/[]/*!*/ args) {
     return PythonProtocol.Operation(operation, ArrayUtils.Insert(this, args));
 }
Esempio n. 25
0
 public virtual MetaObject BindOperation(OperationBinder binder, params MetaObject[] args)
 {
     ContractUtils.RequiresNotNull(binder, "binder");
     return(binder.FallbackOperation(this, args));
 }
        private static DynamicMetaObject/*!*/ MakeDocumentationOperation(OperationBinder/*!*/ operation, DynamicMetaObject/*!*/[]/*!*/ args) {
            BinderState state = BinderState.GetBinderState(operation);

            return new DynamicMetaObject(
                Binders.Get(
                    BinderState.GetCodeContext(operation),
                    state,
                    typeof(string),
                    "__doc__",
                    args[0].Expression
                ),
                args[0].Restrictions
            );
        }
        /// <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 DynamicMetaObject/*!*/ MakeContainsOperation(OperationBinder/*!*/ operation, DynamicMetaObject/*!*/[]/*!*/ types) {
            DynamicMetaObject 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);

                ParameterExpression curIndex = Ast.Variable(typeof(int), "count");
                sf = SlotOrFunction.GetSlotOrFunction(state, Symbols.GetItem, types[0], new DynamicMetaObject(curIndex, BindingRestrictions.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 DynamicMetaObject(
                        Ast.Block(
                            new ParameterExpression[] { curIndex, getItemRes, containsRes },
                            Utils.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 PythonOperationBinder(
                                                state,
                                                StandardOperators.Equal
                                            ),
                                            typeof(bool),
                                            types[1].Expression,
                                            getItemRes
                                        ),
                                        Ast.Assign(containsRes, Ast.Constant(true)),
                                        Ast.Break(target)
                                    ),
                                    Ast.Empty()
                                ),
                                null,                                               // loop else
                                target,                                             // break label target
                                null
                            ),
                            containsRes
                        ),
                        BindingRestrictions.Combine(types)
                    );
                } else {
                    sf = SlotOrFunction.GetSlotOrFunction(state, Symbols.Iterator, types[0]);
                    if (sf.Success) {
                        // iterate using __iter__
                        res = new DynamicMetaObject(
                            Ast.Call(
                                typeof(PythonOps).GetMethod("ContainsFromEnumerable"),
                                Ast.Constant(state.Context),
                                Ast.Dynamic(
                                    new ConversionBinder(
                                        state,
                                        typeof(IEnumerator),
                                        ConversionResultKind.ExplicitCast
                                    ),
                                    typeof(IEnumerator),
                                    sf.Target.Expression
                                ),
                                AstUtils.Convert(types[1].Expression, typeof(object))
                            ),
                            BindingRestrictions.Combine(types)
                        );
                    } else {
                        // non-iterable object
                        res = new DynamicMetaObject(
                            Ast.Throw(
                                Ast.Call(
                                    typeof(PythonOps).GetMethod("TypeErrorForNonIterableObject"),
                                    AstUtils.Convert(
                                        types[1].Expression,
                                        typeof(object)
                                    )
                                )
                            ),
                            BindingRestrictions.Combine(types)
                        );
                    }
                }
            }

            if (res.GetLimitType() != typeof(bool) && res.GetLimitType() != typeof(void)) {
                res = new DynamicMetaObject(
                    Binders.Convert(
                        state,
                        typeof(bool),
                        ConversionResultKind.ExplicitCast,
                        res.Expression
                    ),
                    res.Restrictions
                );
            }

            return res;
        }
Esempio n. 28
0
 public virtual DynamicMetaObject BindOperation(OperationBinder binder, params DynamicMetaObject[] args)
 {
     ContractUtils.RequiresNotNull(binder, nameof(binder));
     return(binder.FallbackOperation(this, args));
 }
        private static DynamicMetaObject/*!*/ MakeMemberNamesOperation(OperationBinder/*!*/ operation, DynamicMetaObject[] args) {
            DynamicMetaObject self = args[0];
            CodeContext context;
            if (args.Length > 1 && args[0].GetLimitType() == typeof(CodeContext)) {
                self = args[1];
                context = (CodeContext)args[0].Value;
            } else {
                context = BinderState.GetBinderState(operation).Context;
            }

            if (typeof(IMembersList).IsAssignableFrom(self.GetLimitType())) {
                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 DynamicMetaObject(
                    Ast.Constant(strNames),
                    BindingRestrictions.GetInstanceRestriction(self.Expression, self.Value).Merge(self.Restrictions)
                );
            }

            return new DynamicMetaObject(
                Ast.Constant(strNames),
                BindingRestrictions.GetInstanceRestriction(self.Expression, self.Value).Merge(self.Restrictions)
            );
        }