/// <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); }
/// <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); }
/// <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); }
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); } } }
/// <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; }
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); }
/// <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; }
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); }