internal override void MakeGetExpression(PythonBinder/*!*/ binder, Expression/*!*/ codeContext, DynamicMetaObject instance, DynamicMetaObject/*!*/ owner, ConditionalBuilder/*!*/ builder) { if (instance != null) { builder.FinishCondition( Ast.Call( typeof(PythonOps).GetMethod(nameof(PythonOps.MakeBoundBuiltinFunction)), AstUtils.Constant(_template), instance.Expression ) ); } else { builder.FinishCondition(AstUtils.Constant(this)); } }
internal override void MakeGetExpression(PythonBinder /*!*/ binder, Expression /*!*/ codeContext, DynamicMetaObject instance, DynamicMetaObject /*!*/ owner, ConditionalBuilder /*!*/ builder) { if (Getter.Length != 0 && !Getter[0].IsPublic) { // fallback to runtime call base.MakeGetExpression(binder, codeContext, instance, owner, builder); } else if (NeedToReturnProperty(instance, Getter)) { builder.FinishCondition(AstUtils.Constant(this)); } else if (Getter[0].ContainsGenericParameters) { builder.FinishCondition( DefaultBinder.MakeError( binder.MakeContainsGenericParametersError( MemberTracker.FromMemberInfo(_info) ), typeof(object) ).Expression ); } else if (instance != null) { builder.FinishCondition( AstUtils.Convert( binder.MakeCallExpression( new PythonOverloadResolverFactory(binder, codeContext), Getter[0], instance ).Expression, typeof(object) ) ); } else { builder.FinishCondition( AstUtils.Convert( binder.MakeCallExpression( new PythonOverloadResolverFactory(binder, codeContext), Getter[0] ).Expression, typeof(object) ) ); } }
/// <summary> /// Gets an expression which is used for accessing this slot. If the slot lookup fails the error expression /// is used again. /// /// The default implementation just calls the TryGetValue method. Subtypes of PythonTypeSlot can override /// this and provide a more optimal implementation. /// </summary> internal virtual void MakeGetExpression(PythonBinder /*!*/ binder, Expression /*!*/ codeContext, DynamicMetaObject instance, DynamicMetaObject /*!*/ owner, ConditionalBuilder /*!*/ builder) { ParameterExpression tmp = Ast.Variable(typeof(object), "slotTmp"); Expression call = Ast.Call( typeof(PythonOps).GetMethod("SlotTryGetValue"), codeContext, AstUtils.Convert(AstUtils.WeakConstant(this), typeof(PythonTypeSlot)), instance != null ? instance.Expression : AstUtils.Constant(null), owner.Expression, tmp ); builder.AddVariable(tmp); if (!GetAlwaysSucceeds) { builder.AddCondition( call, tmp ); } else { builder.FinishCondition(Ast.Block(call, tmp)); } }
internal override void MakeGetExpression(PythonBinder /*!*/ binder, Expression /*!*/ codeContext, DynamicMetaObject instance, DynamicMetaObject /*!*/ owner, ConditionalBuilder /*!*/ builder) { if (!_info.IsPublic || _info.DeclaringType.ContainsGenericParameters) { // fallback to reflection base.MakeGetExpression(binder, codeContext, instance, owner, builder); } else if (instance == null) { if (_info.IsStatic) { builder.FinishCondition(AstUtils.Convert(Ast.Field(null, _info), typeof(object))); } else { builder.FinishCondition(Ast.Constant(this)); } } else { builder.FinishCondition( AstUtils.Convert( Ast.Field( binder.ConvertExpression( instance.Expression, _info.DeclaringType, ConversionResultKind.ExplicitCast, new PythonOverloadResolverFactory(binder, codeContext) ), _info ), typeof(object) ) ); } }
internal override void MakeGetExpression(PythonBinder /*!*/ binder, Expression /*!*/ codeContext, DynamicMetaObject instance, DynamicMetaObject /*!*/ owner, ConditionalBuilder /*!*/ builder) { builder.FinishCondition(Ast.Constant(this)); }
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; } }
private static void MakeCompareReturn(ConditionalBuilder/*!*/ bodyBuilder, Expression retCondition, Expression/*!*/ retValue, bool isReverse, Type retType) { if (retCondition != null) { bodyBuilder.AddCondition(retCondition, retValue); } else { bodyBuilder.FinishCondition(retValue, retType); } }
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); }
public override DynamicMetaObject/*!*/ CompleteRuleTarget(DynamicMetaObjectBinder/*!*/ metaBinder, DynamicMetaObject/*!*/[]/*!*/ args, Func<DynamicMetaObject> customFailure) { ConditionalBuilder cb = new ConditionalBuilder(); _slot.MakeGetExpression( Binder, AstUtils.Constant(PythonContext.SharedContext), args[0], new DynamicMetaObject( Ast.Call( typeof(DynamicHelpers).GetMethod("GetPythonType"), AstUtils.Convert(args[0].Expression, typeof(object)) ), BindingRestrictions.Empty, DynamicHelpers.GetPythonType(args[0].Value) ), cb ); if (!cb.IsFinal) { cb.FinishCondition(metaBinder.Throw(Ast.New(typeof(InvalidOperationException)))); } Expression callable = cb.GetMetaObject().Expression; Expression[] exprArgs = new Expression[args.Length - 1]; for (int i = 1; i < args.Length; i++) { exprArgs[i - 1] = args[i].Expression; } Expression retVal = DynamicExpression.Dynamic( PythonContext.Invoke( new CallSignature(exprArgs.Length) ), typeof(object), ArrayUtils.Insert(AstUtils.Constant(PythonContext.SharedContext), (Expression)callable, exprArgs) ); if (IsSetter) { retVal = Ast.Block(retVal, args[args.Length - 1].Expression); } return new DynamicMetaObject( retVal, BindingRestrictions.Combine(args) ); }
/// <summary> /// Makes the comparison rule which returns an int (-1, 0, 1). TODO: Better name? /// </summary> private static DynamicMetaObject/*!*/ MakeSortComparisonRule(DynamicMetaObject/*!*/[]/*!*/ types, DynamicMetaObjectBinder/*!*/ operation, PythonOperationKind op) { RestrictTypes(types); DynamicMetaObject fastPath = FastPathCompare(types); if (fastPath != null) { return fastPath; } // 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; PythonContext state = PythonContext.GetPythonContext(operation); cfunc = SlotOrFunction.GetSlotOrFunction(state, "__cmp__", types); rcfunc = SlotOrFunction.GetSlotOrFunction(state, "__cmp__", rTypes); eqfunc = SlotOrFunction.GetSlotOrFunction(state, "__eq__", types); reqfunc = SlotOrFunction.GetSlotOrFunction(state, "__eq__", rTypes); ltfunc = SlotOrFunction.GetSlotOrFunction(state, "__lt__", types); gtfunc = SlotOrFunction.GetSlotOrFunction(state, "__gt__", types); rltfunc = SlotOrFunction.GetSlotOrFunction(state, "__lt__", rTypes); rgtfunc = SlotOrFunction.GetSlotOrFunction(state, "__gt__", 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( AstUtils.Constant(0), BindingRestrictions.Combine(types) ); } else if (yType.UnderlyingSystemType.IsPrimitive() || yType.UnderlyingSystemType == typeof(BigInteger)) { return new DynamicMetaObject( AstUtils.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(state, op, types[0], types[1], true)) { // need to try __coerce__ first. DoCoerce(state, bodyBuilder, PythonOperationKind.Compare, types, false); } more = more && MakeOneCompareGeneric(cTarget, false, types, MakeCompareReverse, bodyBuilder, typeof(int)); 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, typeof(int)); 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, typeof(int)); MakeOneCompareGeneric(reqTarget, true, types, MakeCompareToZero, bodyBuilder, typeof(int)); // try less than & reverse MakeOneCompareGeneric(ltTarget, false, types, MakeCompareToNegativeOne, bodyBuilder, typeof(int)); MakeOneCompareGeneric(rgtTarget, true, types, MakeCompareToNegativeOne, bodyBuilder, typeof(int)); // try greater than & reverse MakeOneCompareGeneric(gtTarget, false, types, MakeCompareToOne, bodyBuilder, typeof(int)); MakeOneCompareGeneric(rltTarget, true, types, MakeCompareToOne, bodyBuilder, typeof(int)); } if (xType != yType) { if (more && ShouldCoerce(state, op, types[0], types[1], true)) { // need to try __coerce__ first. DoCoerce(state, bodyBuilder, PythonOperationKind.Compare, types, false); } more = more && MakeOneCompareGeneric(cTarget, false, types, MakeCompareReverse, bodyBuilder, typeof(int)); if (more && ShouldCoerce(state, op, types[1], types[0], true)) { // try __coerce__ first DoCoerce(state, bodyBuilder, PythonOperationKind.Compare, rTypes, true, delegate(Expression e) { return ReverseCompareValue(e); }); } more = more && MakeOneCompareGeneric(rcTarget, true, types, MakeCompareReverse, bodyBuilder, typeof(int)); } if (more) { // fall back to compare types bodyBuilder.FinishCondition(MakeFallbackCompare(operation, op, types), typeof(int)); } return bodyBuilder.GetMetaObject(types); }
private static DynamicMetaObject/*!*/ MakeComparisonOperation(DynamicMetaObject/*!*/[]/*!*/ types, DynamicMetaObjectBinder/*!*/ operation, PythonOperationKind opString, DynamicMetaObject errorSuggestion) { RestrictTypes(types); PythonOperationKind op = NormalizeOperator(opString); PythonContext state = PythonContext.GetPythonContext(operation); Debug.Assert(types.Length == 2); DynamicMetaObject xType = types[0], yType = types[1]; string opSym = Symbols.OperatorToSymbol(op); string 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, "__cmp__", types); rcmp = SlotOrFunction.GetSlotOrFunction(state, "__cmp__", rTypes); ConditionalBuilder bodyBuilder = new ConditionalBuilder(operation); SlotOrFunction.GetCombinedTargets(fop, rop, out fop, out rop); SlotOrFunction.GetCombinedTargets(cmp, rcmp, out cmp, out rcmp); bool shouldWarn = false; WarningInfo info = null; // first try __op__ or __rop__ and return the value shouldWarn = fop.ShouldWarn(state, out info); if (MakeOneCompareGeneric(fop, false, types, MakeCompareReturn, bodyBuilder, typeof(object))) { shouldWarn = shouldWarn || rop.ShouldWarn(state, out info); if (MakeOneCompareGeneric(rop, true, types, MakeCompareReturn, bodyBuilder, typeof(object))) { // then try __cmp__ or __rcmp__ and compare the resulting int appropriaetly shouldWarn = shouldWarn || cmp.ShouldWarn(state, out info); if (ShouldCoerce(state, opString, xType, yType, true)) { DoCoerce(state, bodyBuilder, PythonOperationKind.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, Type retType) { MakeCompareTest(op, builder, retCond, expr, reverse, retType); }, bodyBuilder, typeof(object))) { shouldWarn = shouldWarn || rcmp.ShouldWarn(state, out info); if (ShouldCoerce(state, opString, yType, xType, true)) { DoCoerce(state, bodyBuilder, PythonOperationKind.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, Type retType) { MakeCompareTest(op, builder, retCond, expr, reverse, retType); }, bodyBuilder, typeof(object))) { if (errorSuggestion != null) { bodyBuilder.FinishCondition(errorSuggestion.Expression, typeof(object)); } else { bodyBuilder.FinishCondition(BindingHelpers.AddPythonBoxing(MakeFallbackCompare(operation, op, types)), typeof(object)); } } } } } DynamicMetaObject res = bodyBuilder.GetMetaObject(types); if (!shouldWarn || res == null) { return res; } else { return info.AddWarning(Ast.Constant(state.SharedContext), res); } }
internal override void MakeGetExpression(PythonBinder/*!*/ binder, Expression/*!*/ codeContext, DynamicMetaObject instance, DynamicMetaObject/*!*/ owner, ConditionalBuilder/*!*/ builder) { if (Getter.Length != 0 && !Getter[0].IsPublic) { // fallback to runtime call base.MakeGetExpression(binder, codeContext, instance, owner, builder); } else if (NeedToReturnProperty(instance, Getter)) { builder.FinishCondition(AstUtils.Constant(this)); } else if (Getter[0].ContainsGenericParameters) { builder.FinishCondition( DefaultBinder.MakeError( binder.MakeContainsGenericParametersError( MemberTracker.FromMemberInfo(_info) ), typeof(object) ).Expression ); } else if (instance != null) { builder.FinishCondition( AstUtils.Convert( binder.MakeCallExpression( new PythonOverloadResolverFactory(binder, codeContext), Getter[0], instance ).Expression, typeof(object) ) ); } else { builder.FinishCondition( AstUtils.Convert( binder.MakeCallExpression( new PythonOverloadResolverFactory(binder, codeContext), Getter[0] ).Expression, typeof(object) ) ); } }
internal static DynamicMetaObject Call(DynamicMetaObjectBinder/*!*/ call, DynamicMetaObject target, DynamicMetaObject/*!*/[]/*!*/ args) { Assert.NotNull(call, args); Assert.NotNullItems(args); if (target.NeedsDeferral()) { return call.Defer(ArrayUtils.Insert(target, args)); } foreach (DynamicMetaObject mo in args) { if (mo.NeedsDeferral()) { RestrictTypes(args); return call.Defer( ArrayUtils.Insert(target, args) ); } } DynamicMetaObject self = target.Restrict(target.GetLimitType()); ValidationInfo valInfo = BindingHelpers.GetValidationInfo(target); PythonType pt = DynamicHelpers.GetPythonType(target.Value); BinderState state = BinderState.GetBinderState(call); // look for __call__, if it's present dispatch to it. Otherwise fall back to the // default binder PythonTypeSlot callSlot; if (!typeof(Delegate).IsAssignableFrom(target.GetLimitType()) && pt.TryResolveSlot(state.Context, Symbols.Call, out callSlot)) { ConditionalBuilder cb = new ConditionalBuilder(call); Expression body; callSlot.MakeGetExpression( state.Binder, BinderState.GetCodeContext(call), self.Expression, GetPythonType(self), cb ); if (!cb.IsFinal) { cb.FinishCondition(GetCallError(self)); } Expression[] callArgs = ArrayUtils.Insert( BinderState.GetCodeContext(call), cb.GetMetaObject().Expression, DynamicUtils.GetExpressions(args) ); body = Ast.Dynamic( BinderState.GetBinderState(call).Invoke( BindingHelpers.GetCallSignature(call) ), typeof(object), callArgs ); return BindingHelpers.AddDynamicTestAndDefer( call, new DynamicMetaObject(body, self.Restrictions.Merge(BindingRestrictions.Combine(args))), args, valInfo ); } return null; }
internal override void MakeGetExpression(PythonBinder/*!*/ binder, Expression/*!*/ codeContext, DynamicMetaObject instance, DynamicMetaObject/*!*/ owner, ConditionalBuilder/*!*/ builder) { if (!_info.IsPublic || _info.DeclaringType.ContainsGenericParameters) { // fallback to reflection base.MakeGetExpression(binder, codeContext, instance, owner, builder); } else if (instance == null) { if (_info.IsStatic) { builder.FinishCondition(AstUtils.Convert(Ast.Field(null, _info), typeof(object))); } else { builder.FinishCondition(Ast.Constant(this)); } } else { builder.FinishCondition( AstUtils.Convert( Ast.Field( binder.ConvertExpression( instance.Expression, _info.DeclaringType, ConversionResultKind.ExplicitCast, new PythonOverloadResolverFactory(binder, codeContext) ), _info ), typeof(object) ) ); } }
private static DynamicMetaObject/*!*/ MakeComparisonOperation(DynamicMetaObject/*!*/[]/*!*/ types, DynamicMetaObjectBinder/*!*/ operation, PythonOperationKind opString) { RestrictTypes(types); PythonOperationKind op = NormalizeOperator(opString); 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(state, opString, xType, yType, true)) { DoCoerce(state, bodyBuilder, PythonOperationKind.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(state, opString, yType, xType, true)) { DoCoerce(state, bodyBuilder, PythonOperationKind.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); }