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); }
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); }
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); }
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); }
public override bool Equals(object obj) { OperationBinder oa = obj as OperationBinder; return(oa != null && oa._operation == _operation); }
public override DynamicMetaObject/*!*/ BindOperation(OperationBinder/*!*/ operation, params DynamicMetaObject/*!*/[]/*!*/ args) { return PythonProtocol.Operation(operation, ArrayUtils.Insert(this, args)); }
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; }
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) ); }