Provides an abstraction for calling something which might be a builtin function or might be some arbitrary user defined slot. If the object is a builtin function the call will go directly to the underlying .NET method. If the object is an arbitrary callable object we will setup a nested dynamic site for performing the additional dispatch. TODO: We could probably do a specific binding to the object if it's another IDyanmicObject.
Пример #1
0
        /// <summary>
        /// Combines two methods, which came from two different binary types, selecting the method which has the best
        /// set of conversions (the conversions which result in the least narrowing).
        /// </summary>
        public static bool GetCombinedTargets(SlotOrFunction fCand, SlotOrFunction rCand, out SlotOrFunction fTarget, out SlotOrFunction rTarget)
        {
            fTarget = rTarget = Empty;

            if (fCand.Success)
            {
                if (rCand.Success)
                {
                    if (fCand.NarrowingLevel <= rCand.NarrowingLevel)
                    {
                        fTarget = fCand;
                        rTarget = rCand;
                    }
                    else
                    {
                        fTarget = Empty;
                        rTarget = rCand;
                    }
                }
                else
                {
                    fTarget = fCand;
                }
            }
            else if (rCand.Success)
            {
                rTarget = rCand;
            }
            else
            {
                return(false);
            }

            return(true);
        }
Пример #2
0
        /// <summary>
        /// Gets a MetaObject which converts the provided object to a bool using __bool__ or __len__
        /// protocol methods.  This code is shared between both our fallback for a site and our MetaObject
        /// for user defined objects.
        /// </summary>
        internal static DynamicMetaObject ConvertToBool(DynamicMetaObjectBinder /*!*/ conversion, DynamicMetaObject /*!*/ self)
        {
            Assert.NotNull(conversion, self);

            SlotOrFunction sf = SlotOrFunction.GetSlotOrFunction(
                PythonContext.GetPythonContext(conversion),
                "__bool__",
                self);

            if (sf.Success)
            {
                if (sf.Target.Expression.Type != typeof(bool))
                {
                    return(new DynamicMetaObject(
                               Ast.Call(
                                   typeof(PythonOps).GetMethod(nameof(PythonOps.ThrowingConvertToBool)),
                                   sf.Target.Expression
                                   ),
                               sf.Target.Restrictions
                               ));
                }

                return(sf.Target);
            }

            sf = SlotOrFunction.GetSlotOrFunction(
                PythonContext.GetPythonContext(conversion),
                "__len__",
                self);

            if (sf.Success)
            {
                return(new DynamicMetaObject(
                           GetConvertByLengthBody(
                               PythonContext.GetPythonContext(conversion),
                               sf.Target.Expression
                               ),
                           sf.Target.Restrictions
                           ));
            }

            return(null);
        }
Пример #3
0
        /// <summary>
        /// Tries to get a MethodBinder associated with the slot for the specified type.
        ///
        /// If a method is found the binder is set and true is returned.
        /// If nothing is found binder is null and true is returned.
        /// If something other than a method is found false is returned.
        ///
        /// TODO: Remove rop
        /// </summary>
        internal static bool TryGetBinder(PythonContext /*!*/ state, DynamicMetaObject /*!*/[] /*!*/ types, string op, string rop, out SlotOrFunction /*!*/ res, out PythonType declaringType)
        {
            declaringType = null;

            DynamicMetaObject xType = types[0];
            BuiltinFunction   xBf;

            if (!BindingHelpers.TryGetStaticFunction(state, op, xType, out xBf))
            {
                res = SlotOrFunction.Empty;
                return(false);
            }

            xBf = CheckAlwaysNotImplemented(xBf);

            BindingTarget     bt;
            DynamicMetaObject binder;
            DynamicMetaObject yType = null;
            BuiltinFunction   yBf   = null;

            if (types.Length > 1)
            {
                yType = types[1];
                if (!BindingHelpers.IsSubclassOf(xType, yType) && !BindingHelpers.TryGetStaticFunction(state, rop, yType, out yBf))
                {
                    res = SlotOrFunction.Empty;
                    return(false);
                }

                yBf = CheckAlwaysNotImplemented(yBf);
            }

            if (yBf == xBf)
            {
                yBf = null;
            }
            else if (yBf != null && BindingHelpers.IsSubclassOf(yType, xType))
            {
                xBf = null;
            }

            var mc = new PythonOverloadResolver(
                state.Binder,
                types,
                new CallSignature(types.Length),
                AstUtils.Constant(state.SharedContext)
                );

            if (xBf == null)
            {
                if (yBf == null)
                {
                    binder = null;
                    bt     = null;
                }
                else
                {
                    declaringType = DynamicHelpers.GetPythonTypeFromType(yBf.DeclaringType);
                    binder        = state.Binder.CallMethod(mc, yBf.Targets, BindingRestrictions.Empty, null, PythonNarrowing.None, PythonNarrowing.BinaryOperator, out bt);
                }
            }
            else
            {
                if (yBf == null)
                {
                    declaringType = DynamicHelpers.GetPythonTypeFromType(xBf.DeclaringType);
                    binder        = state.Binder.CallMethod(mc, xBf.Targets, BindingRestrictions.Empty, null, PythonNarrowing.None, PythonNarrowing.BinaryOperator, out bt);
                }
                else
                {
                    List <MethodBase> targets = new List <MethodBase>();
                    targets.AddRange(xBf.Targets);
                    foreach (MethodBase mb in yBf.Targets)
                    {
                        if (!ContainsMethodSignature(targets, mb))
                        {
                            targets.Add(mb);
                        }
                    }

                    binder = state.Binder.CallMethod(mc, targets.ToArray(), BindingRestrictions.Empty, null, PythonNarrowing.None, PythonNarrowing.BinaryOperator, out bt);

                    foreach (MethodBase mb in yBf.Targets)
                    {
                        if (bt.Overload.ReflectionInfo == mb)
                        {
                            declaringType = DynamicHelpers.GetPythonTypeFromType(yBf.DeclaringType);
                            break;
                        }
                    }

                    if (declaringType == null)
                    {
                        declaringType = DynamicHelpers.GetPythonTypeFromType(xBf.DeclaringType);
                    }
                }
            }

            if (binder != null)
            {
                res = new SlotOrFunction(bt, binder);
            }
            else
            {
                res = SlotOrFunction.Empty;
            }

            Debug.Assert(res != null);
            return(true);
        }
Пример #4
0
        internal static bool TryGetBinder(PythonContext /*!*/ state, DynamicMetaObject /*!*/[] /*!*/ types, string op, string rop, out SlotOrFunction /*!*/ res)
        {
            PythonType declType;

            return(TryGetBinder(state, types, op, rop, out res, out declType));
        }
        private static bool MakeOneTarget(PythonContext/*!*/ state, SlotOrFunction/*!*/ target, PythonTypeSlot slotTarget, ConditionalBuilder/*!*/ bodyBuilder, bool reverse, DynamicMetaObject/*!*/[]/*!*/ 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, typeof(object));
                return false;
            }
        }
        /// <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, DynamicMetaObject/*!*/[]/*!*/ types, ComparisonHelper returner, ConditionalBuilder/*!*/ bodyBuilder, Type retType) {
            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,
                        AstUtils.Constant(PythonOps.NotImplemented)
                    ),
                    tmp,
                    reverse,
                    retType);
                return true;
            } else {
                returner(
                    bodyBuilder,
                    null,
                    target.Target.Expression,
                    reverse,
                    retType
                );
                return false;
            }
        }
        private static DynamicMetaObject/*!*/ MakeBinaryOperatorResult(DynamicMetaObject/*!*/[]/*!*/ types, DynamicMetaObjectBinder/*!*/ operation, PythonOperationKind op, SlotOrFunction/*!*/ fCand, SlotOrFunction/*!*/ rCand, PythonTypeSlot fSlot, PythonTypeSlot rSlot, DynamicMetaObject errorSuggestion) {
            Assert.NotNull(operation, fCand, rCand);

            SlotOrFunction fTarget, rTarget;
            PythonContext state = PythonContext.GetPythonContext(operation);

            ConditionalBuilder bodyBuilder = new ConditionalBuilder(operation);

            if ((op & PythonOperationKind.InPlace) != 0) {
                // in place operator, see if there's a specific method that handles it.
                SlotOrFunction function = SlotOrFunction.GetSlotOrFunction(PythonContext.GetPythonContext(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, typeof(object))) {
                    // 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(state, op, types[0], types[1], false) &&
                !ShouldCoerce(state, op, types[1], types[0], false) &&
                bodyBuilder.NoConditions) {
                return MakeRuleForNoMatch(operation, op, errorSuggestion, types);
            }

            if (ShouldCoerce(state, op, types[0], types[1], false) &&
                (op != PythonOperationKind.Mod || !MetaPythonObject.GetPythonType(types[0]).IsSubclassOf(TypeCache.String))) {
                // need to try __coerce__ first.
                DoCoerce(state, bodyBuilder, op, types, false);
            }

            if (MakeOneTarget(PythonContext.GetPythonContext(operation), fTarget, fSlot, bodyBuilder, false, types)) {
                if (ShouldCoerce(state, op, types[1], types[0], false)) {
                    // need to try __coerce__ on the reverse first                    
                    DoCoerce(state, bodyBuilder, op, new DynamicMetaObject[] { types[1], types[0] }, true);
                }

                if (rSlot != null) {
                    MakeSlotCall(PythonContext.GetPythonContext(operation), types, bodyBuilder, rSlot, true);
                    bodyBuilder.FinishCondition(MakeBinaryThrow(operation, op, types).Expression, typeof(object));
                } else if (MakeOneTarget(PythonContext.GetPythonContext(operation), rTarget, rSlot, bodyBuilder, false, types)) {
                    // need to fallback to throwing or coercion
                    bodyBuilder.FinishCondition(MakeBinaryThrow(operation, op, types).Expression, typeof(object));
                }
            }

            return bodyBuilder.GetMetaObject(types);
        }
        private static void GetOperatorMethods(DynamicMetaObject/*!*/[]/*!*/ types, PythonOperationKind oper, PythonContext state, out SlotOrFunction fbinder, out SlotOrFunction rbinder, out PythonTypeSlot fSlot, out PythonTypeSlot rSlot) {
            oper = NormalizeOperator(oper);
            oper &= ~PythonOperationKind.InPlace;

            string op, rop;
            if (!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 == PythonOperationKind.Multiply &&
                IsSequence(types[0]) &&
                !PythonOps.IsNonExtensibleNumericType(types[1].GetLimitType())) {
                // 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__.

                DynamicMetaObject[] newTypes = new DynamicMetaObject[2];
                newTypes[0] = types[0];
                newTypes[1] = new DynamicMetaObject(
                    Ast.New(
                        typeof(Index).GetConstructor(new Type[] { typeof(object) }),
                        AstUtils.Convert(types[1].Expression, typeof(object))
                    ),
                    BindingRestrictions.Empty
                );
                types = newTypes;
            }

            if (!SlotOrFunction.TryGetBinder(state, types, op, null, out fbinder, out fParent)) {
                foreach (PythonType pt in MetaPythonObject.GetPythonType(types[0]).ResolutionOrder) {
                    if (pt.TryLookupSlot(state.SharedContext, op, out fSlot)) {
                        fParent = pt;
                        break;
                    }
                }
            }

            if (!SlotOrFunction.TryGetBinder(state, types, null, rop, out rbinder, out rParent)) {
                foreach (PythonType pt in MetaPythonObject.GetPythonType(types[1]).ResolutionOrder) {
                    if (pt.TryLookupSlot(state.SharedContext, 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 == "__truediv__" || op == "__rtruediv__") {
                    // true div on a type which doesn't support it, go ahead and try normal divide
                    PythonOperationKind newOp = op == "__truediv__" ? PythonOperationKind.Divide : PythonOperationKind.ReverseDivide;

                    GetOperatorMethods(types, newOp, state, out fbinder, out rbinder, out fSlot, out rSlot);
                }
            }
        }
Пример #9
0
        /// <summary>
        /// Tries to get a MethodBinder associated with the slot for the specified type.
        /// 
        /// If a method is found the binder is set and true is returned.
        /// If nothing is found binder is null and true is returned.
        /// If something other than a method is found false is returned.
        /// 
        /// TODO: Remove rop
        /// </summary>
        internal static bool TryGetBinder(PythonContext/*!*/ state, DynamicMetaObject/*!*/[]/*!*/ types, string op, string rop, out SlotOrFunction/*!*/ res, out PythonType declaringType) {
            declaringType = null;

            DynamicMetaObject xType = types[0];
            BuiltinFunction xBf;
            if (!BindingHelpers.TryGetStaticFunction(state, op, xType, out xBf)) {
                res = SlotOrFunction.Empty;
                return false;
            }

            xBf = CheckAlwaysNotImplemented(xBf);

            BindingTarget bt;
            DynamicMetaObject binder;
            DynamicMetaObject yType = null;
            BuiltinFunction yBf = null;

            if (types.Length > 1) {
                yType = types[1];
                if (!BindingHelpers.IsSubclassOf(xType, yType) && !BindingHelpers.TryGetStaticFunction(state, rop, yType, out yBf)) {
                    res = SlotOrFunction.Empty;
                    return false;
                }

                yBf = CheckAlwaysNotImplemented(yBf);
            }

            if (yBf == xBf) {
                yBf = null;
            } else if (yBf != null && BindingHelpers.IsSubclassOf(yType, xType)) {
                xBf = null;
            }

            var mc = new PythonOverloadResolver(
                state.Binder, 
                types,
                new CallSignature(types.Length),                
                AstUtils.Constant(state.SharedContext)
            );

            if (xBf == null) {
                if (yBf == null) {
                    binder = null;
                    bt = null;
                } else {
                    declaringType = DynamicHelpers.GetPythonTypeFromType(yBf.DeclaringType);
                    binder = state.Binder.CallMethod(mc, yBf.Targets, BindingRestrictions.Empty, null, PythonNarrowing.None, PythonNarrowing.BinaryOperator, out bt);
                }
            } else {
                if (yBf == null) {
                    declaringType = DynamicHelpers.GetPythonTypeFromType(xBf.DeclaringType);
                    binder = state.Binder.CallMethod(mc, xBf.Targets, BindingRestrictions.Empty, null, PythonNarrowing.None, PythonNarrowing.BinaryOperator, out bt);
                } else {
                    List<MethodBase> targets = new List<MethodBase>();
                    targets.AddRange(xBf.Targets);
                    foreach (MethodBase mb in yBf.Targets) {
                        if (!ContainsMethodSignature(targets, mb)) targets.Add(mb);
                    }

                    binder = state.Binder.CallMethod(mc, targets.ToArray(), BindingRestrictions.Empty, null, PythonNarrowing.None, PythonNarrowing.BinaryOperator, out bt);

                    foreach (MethodBase mb in yBf.Targets) {
                        if (bt.Overload.ReflectionInfo == mb) {
                            declaringType = DynamicHelpers.GetPythonTypeFromType(yBf.DeclaringType);
                            break;
                        }
                    }

                    if (declaringType == null) {
                        declaringType = DynamicHelpers.GetPythonTypeFromType(xBf.DeclaringType);
                    }
                }
            }

            if (binder != null) {
                res = new SlotOrFunction(bt, binder);
            } else {
                res = SlotOrFunction.Empty;
            }

            Debug.Assert(res != null);
            return true;
        }
Пример #10
0
 internal static bool TryGetBinder(PythonContext/*!*/ state, DynamicMetaObject/*!*/[]/*!*/ types, string op, string rop, out SlotOrFunction/*!*/ res) {
     PythonType declType;
     return TryGetBinder(state, types, op, rop, out res, out declType);
 }
Пример #11
0
        /// <summary>
        /// Combines two methods, which came from two different binary types, selecting the method which has the best
        /// set of conversions (the conversions which result in the least narrowing).
        /// </summary>
        public static bool GetCombinedTargets(SlotOrFunction fCand, SlotOrFunction rCand, out SlotOrFunction fTarget, out SlotOrFunction rTarget) {
            fTarget = rTarget = Empty;

            if (fCand.Success) {
                if (rCand.Success) {
                    if (fCand.NarrowingLevel <= rCand.NarrowingLevel) {
                        fTarget = fCand;
                        rTarget = rCand;
                    } else {
                        fTarget = Empty;
                        rTarget = rCand;
                    }
                } else {
                    fTarget = fCand;
                }
            } else if (rCand.Success) {
                rTarget = rCand;
            } else {
                return false;
            }

            return true;
        }
Пример #12
0
 internal static bool TryGetBinder(BinderState/*!*/ state, DynamicMetaObject/*!*/[]/*!*/ types, SymbolId op, SymbolId rop, out SlotOrFunction/*!*/ res) {
     PythonType declType;
     return TryGetBinder(state, types, op, rop, out res, out declType);
 }