PageRenderTime 94ms CodeModel.GetById 24ms app.highlight 57ms RepoModel.GetById 1ms app.codeStats 1ms

/IronPython_2_0/Src/IronPython/Runtime/Binding/PythonProtocol.Operations.cs

#
C# | 2179 lines | 1640 code | 289 blank | 250 comment | 314 complexity | e22bc4b332e4cb86586b4c1636ae11a6 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/* ****************************************************************************
   2 *
   3 * Copyright (c) Microsoft Corporation. 
   4 *
   5 * This source code is subject to terms and conditions of the Microsoft Public License. A 
   6 * copy of the license can be found in the License.html file at the root of this distribution. If 
   7 * you cannot locate the  Microsoft Public License, please send an email to 
   8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
   9 * by the terms of the Microsoft Public License.
  10 *
  11 * You must not remove this notice, or any other, from this software.
  12 *
  13 *
  14 * ***************************************************************************/
  15
  16using System; using Microsoft;
  17using System.Collections;
  18using System.Collections.Generic;
  19using System.Diagnostics;
  20using Microsoft.Linq.Expressions;
  21using System.Reflection;
  22using Microsoft.Scripting;
  23using Microsoft.Scripting.Actions;
  24using System.Text;
  25using IronPython.Runtime.Operations;
  26using IronPython.Runtime.Types;
  27using Microsoft.Scripting.Actions.Calls;
  28using Microsoft.Scripting.Ast;
  29using Microsoft.Scripting.Generation;
  30using Microsoft.Scripting.Math;
  31using Microsoft.Scripting.Runtime;
  32using Microsoft.Scripting.Utils;
  33
  34namespace IronPython.Runtime.Binding {
  35    using Ast = Microsoft.Linq.Expressions.Expression;
  36
  37    static partial class PythonProtocol {
  38        private const string DisallowCoerce = "DisallowCoerce";
  39
  40        public static MetaObject/*!*/ Operation(OperationAction/*!*/ operation, params MetaObject/*!*/[]/*!*/ args) {
  41            foreach (MetaObject mo in args) {
  42                if (mo.NeedsDeferral()) {
  43                    return operation.Defer(args);
  44                }
  45            }
  46
  47            ValidationInfo valInfo = BindingHelpers.GetValidationInfo(null, args);
  48
  49            MetaObject res = MakeOperationRule(operation, args);
  50
  51            return BindingHelpers.AddDynamicTestAndDefer(operation, res, args, valInfo);
  52        }
  53
  54        private static MetaObject/*!*/ MakeOperationRule(OperationAction/*!*/ operation, MetaObject/*!*/[]/*!*/ args) {
  55            switch (operation.Operation) {
  56                case StandardOperators.Documentation:
  57                    return MakeDocumentationOperation(operation, args);
  58                case StandardOperators.MemberNames:
  59                    return MakeMemberNamesOperation(operation, args);
  60                case StandardOperators.CallSignatures:
  61                    return MakeCallSignatureOperation(args[0], CompilerHelpers.GetMethodTargets(args[0].Value));
  62                case StandardOperators.IsCallable:
  63                    return MakeIscallableOperation(operation, args);
  64
  65                case StandardOperators.GetItem:
  66                case StandardOperators.SetItem:
  67                case StandardOperators.GetSlice:
  68                case StandardOperators.SetSlice:
  69                case StandardOperators.DeleteItem:
  70                case StandardOperators.DeleteSlice:
  71                    // Indexers need to see if the index argument is an expandable tuple.  This will
  72                    // be captured in the AbstractValue in the future but today is captured in the
  73                    // real value.
  74                    return MakeIndexerOperation(operation, args);
  75
  76                case StandardOperators.Not:
  77                    return MakeUnaryNotOperation(operation, args[0]);
  78                case OperatorStrings.Hash:
  79                    return MakeHashOperation(operation, args[0]);
  80
  81                case StandardOperators.Contains:
  82                    return MakeContainsOperation(operation, args);
  83
  84                default:
  85                    if (IsUnary(operation.Operation)) {
  86                        return MakeUnaryOperation(operation, args[0]);
  87                    } else if (IsComparision(operation.Operation)) {
  88                        return MakeComparisonOperation(args, operation);
  89                    }
  90
  91                    return MakeSimpleOperation(args, operation);
  92            }
  93        }
  94
  95        #region Unary Operations
  96
  97        /// <summary>
  98        /// Creates a rule for the contains operator.  This is exposed via "x in y" in 
  99        /// IronPython.  It is implemented by calling the __contains__ method on x and
 100        /// passing in y.  
 101        /// 
 102        /// If a type doesn't define __contains__ but does define __getitem__ then __getitem__ is 
 103        /// called repeatedly in order to see if the object is there.
 104        /// 
 105        /// For normal .NET enumerables we'll walk the iterator and see if it's present.
 106        /// </summary>
 107        private static MetaObject/*!*/ MakeContainsOperation(OperationAction/*!*/ operation, MetaObject/*!*/[]/*!*/ types) {
 108            MetaObject res;
 109            // the paramteres come in backwards from how we look up __contains__, flip them.
 110            Debug.Assert(types.Length == 2);
 111            ArrayUtils.SwapLastTwo(types);
 112
 113            BinderState state = BinderState.GetBinderState(operation);
 114            SlotOrFunction sf = SlotOrFunction.GetSlotOrFunction(state, Symbols.Contains, types);
 115
 116            if (sf.Success) {
 117                // just a call to __contains__
 118                res = sf.Target;
 119            } else {
 120                RestrictTypes(types);
 121
 122                sf = SlotOrFunction.GetSlotOrFunction(state, Symbols.Iterator, types[0]);
 123                if (sf.Success) {
 124                    // iterate using __iter__
 125                    res = new MetaObject(
 126                        Ast.Call(
 127                            typeof(PythonOps).GetMethod("ContainsFromEnumerable"),
 128                            Ast.Constant(state.Context),
 129                            Ast.Dynamic(
 130                                new ConversionBinder(
 131                                    state,
 132                                    typeof(IEnumerator),
 133                                    ConversionResultKind.ExplicitCast
 134                                ),
 135                                typeof(IEnumerator),
 136                                sf.Target.Expression
 137                            ),
 138                            Ast.ConvertHelper(types[1].Expression, typeof(object))
 139                        ),
 140                        Restrictions.Combine(types)
 141                    );
 142                } else {
 143                    ParameterExpression curIndex = Ast.Variable(typeof(int), "count");
 144                    sf = SlotOrFunction.GetSlotOrFunction(state, Symbols.GetItem, types[0], new MetaObject(curIndex, Restrictions.Empty));
 145                    if (sf.Success) {
 146                        // defines __getitem__, need to loop over the indexes and see if we match
 147    
 148                        ParameterExpression getItemRes = Ast.Variable(sf.ReturnType, "getItemRes");
 149                        ParameterExpression containsRes = Ast.Variable(typeof(bool), "containsRes");
 150    
 151                        LabelTarget target = Ast.Label();
 152                        res = new MetaObject(
 153                            Ast.Scope(
 154                                Ast.Comma(
 155                                    Ast.Loop(
 156                                        null,                                                     // test
 157                                        Ast.Assign(curIndex, Ast.Add(curIndex, Ast.Constant(1))), // increment
 158                                        Ast.Block(                                                // body
 159                            // getItemRes = param0.__getitem__(curIndex)
 160                                            Utils.Try(
 161                                                Ast.Assign(
 162                                                    getItemRes,
 163                                                    sf.Target.Expression
 164                                                )
 165                                            ).Catch(
 166                            // end of indexes, return false
 167                                                typeof(IndexOutOfRangeException),
 168                                                Ast.Break(target)
 169                                            ),
 170                            // if(getItemRes == param1) return true
 171                                            Utils.If(
 172                                                Ast.Dynamic(
 173                                                    new OperationBinder(
 174                                                        state,
 175                                                        StandardOperators.Equal
 176                                                    ),
 177                                                    typeof(bool),
 178                                                    types[1].Expression,
 179                                                    getItemRes
 180                                                ),
 181                                                Ast.Assign(containsRes, Ast.Constant(true)),
 182                                                Ast.Break(target)
 183                                            )
 184                                        ),
 185                                        null,                                               // loop else
 186                                        target,                                             // break label target
 187                                        null
 188                                    ),
 189                                    containsRes
 190                                ),
 191                                curIndex,
 192                                getItemRes,
 193                                containsRes
 194                            ),
 195                            Restrictions.Combine(types)
 196                        );
 197                    } else {                    
 198                        // non-iterable object
 199                        res = new MetaObject(
 200                            Ast.Throw(
 201                                Ast.Call(
 202                                    typeof(PythonOps).GetMethod("TypeErrorForNonIterableObject"),
 203                                    Ast.ConvertHelper(
 204                                        types[1].Expression,
 205                                        typeof(object)
 206                                    )
 207                                )
 208                            ),
 209                            Restrictions.Combine(types)
 210                        );
 211                    }                   
 212                }
 213            }
 214
 215            if (res.LimitType != typeof(bool) && res.LimitType != typeof(void)) {
 216                res = new MetaObject(
 217                    Binders.Convert(
 218                        state,
 219                        typeof(bool),
 220                        ConversionResultKind.ExplicitCast,
 221                        res.Expression
 222                    ),
 223                    res.Restrictions
 224                );
 225            }
 226
 227            return res;
 228        }
 229
 230        private static void RestrictTypes(MetaObject/*!*/[] types) {
 231            for (int i = 0; i < types.Length; i++) {
 232                types[i] = types[i].Restrict(types[i].LimitType);
 233            }
 234        }
 235
 236        private static MetaObject/*!*/ MakeHashOperation(OperationAction/*!*/ operation, MetaObject/*!*/ self) {
 237            self = self.Restrict(self.LimitType);
 238
 239            BinderState state = BinderState.GetBinderState(operation);
 240            SlotOrFunction func = SlotOrFunction.GetSlotOrFunction(BinderState.GetBinderState(operation), Symbols.Hash, self);
 241            MetaObject res = func.Target;
 242
 243            if (func.ReturnType != typeof(int)) {
 244                if (func.ReturnType == typeof(BigInteger)) {
 245                    // Python 2.5 defines the result of returning a long as hashing the long
 246                    res = new MetaObject(
 247                        HashBigInt(operation, res.Expression),
 248                        res.Restrictions
 249                    );
 250                } else if (func.ReturnType == typeof(object)) {
 251                    // need to get the integer value here...
 252                    ParameterExpression tempVar = Ast.Parameter(typeof(object), "hashTemp");
 253
 254                    res = new MetaObject(
 255                            Expression.Scope(
 256                                Expression.Comma(
 257                                    Expression.Assign(tempVar, res.Expression),
 258                                    Expression.Condition(
 259                                        Expression.TypeIs(tempVar, typeof(int)),
 260                                        Expression.Convert(tempVar, typeof(int)),
 261                                        Expression.Condition(
 262                                            Expression.TypeIs(tempVar, typeof(BigInteger)),
 263                                            HashBigInt(operation, tempVar),
 264                                            HashConvertToInt(state, tempVar)
 265                                        )
 266                                    )
 267                                ),
 268                                new[] { tempVar }
 269                            ),
 270                            res.Restrictions
 271                        );
 272                } else {
 273                    // need to convert unknown value to object
 274                    res = new MetaObject(
 275                        HashConvertToInt(state, res.Expression),
 276                        res.Restrictions
 277                    );
 278                }
 279            }
 280
 281            return res;
 282        }
 283
 284        private static DynamicExpression/*!*/ HashBigInt(OperationAction/*!*/ operation, Expression/*!*/ expression) {
 285            return Ast.Dynamic(
 286                operation,
 287                typeof(int),
 288                expression
 289            );
 290        }
 291
 292        private static DynamicExpression/*!*/ HashConvertToInt(BinderState/*!*/ state, Expression/*!*/ expression) {
 293            return Ast.Dynamic(
 294                new ConversionBinder(
 295                    state,
 296                    typeof(int),
 297                    ConversionResultKind.ExplicitCast
 298                ),
 299                typeof(int),
 300                expression
 301            );
 302        }
 303
 304
 305        private static MetaObject/*!*/ MakeUnaryOperation(OperationAction/*!*/ operation, MetaObject/*!*/ self) {
 306            self = self.Restrict(self.LimitType);
 307
 308            SlotOrFunction func = SlotOrFunction.GetSlotOrFunction(BinderState.GetBinderState(operation), Symbols.OperatorToSymbol(operation.Operation), self);
 309
 310            if (!func.Success) {
 311                // we get the error message w/ {0} so that PythonBinderHelper.TypeError formats it correctly
 312                return TypeError(operation, MakeUnaryOpErrorMessage(operation.Operation.ToString(), "{0}"), self);
 313            }
 314
 315            return func.Target;
 316        }
 317
 318        private static MetaObject/*!*/ MakeUnaryNotOperation(OperationAction/*!*/ operation, MetaObject/*!*/ self) {
 319            self = self.Restrict(self.LimitType);
 320
 321            SlotOrFunction nonzero = SlotOrFunction.GetSlotOrFunction(BinderState.GetBinderState(operation), Symbols.NonZero, self);
 322            SlotOrFunction length = SlotOrFunction.GetSlotOrFunction(BinderState.GetBinderState(operation), Symbols.Length, self);
 323
 324            Expression notExpr;
 325
 326            if (!nonzero.Success && !length.Success) {
 327                // always False or True for None
 328                notExpr = self.LimitType == typeof(None) ? Ast.True() : Ast.False();
 329            } else {
 330                SlotOrFunction target = nonzero.Success ? nonzero : length;
 331
 332                notExpr = target.Target.Expression;
 333
 334                if (nonzero.Success) {
 335                    // call non-zero and negate it
 336                    if (notExpr.Type == typeof(bool)) {
 337                        notExpr = Ast.Equal(notExpr, Ast.False());
 338                    } else {
 339                        notExpr = Ast.Call(
 340                            typeof(PythonOps).GetMethod("Not"),
 341                            Ast.ConvertHelper(notExpr, typeof(object))
 342                        );
 343                    }
 344                } else {
 345                    // call len, compare w/ zero
 346                    if (notExpr.Type == typeof(int)) {
 347                        notExpr = Ast.Equal(notExpr, Ast.Zero());
 348                    } else {
 349                        notExpr = Ast.Dynamic(
 350                            new OperationBinder(
 351                                BinderState.GetBinderState(operation),
 352                                StandardOperators.Compare
 353                            ),
 354                            typeof(int),
 355                            notExpr,
 356                            Ast.Zero()
 357                        );
 358                    }
 359                }
 360            }
 361
 362            return new MetaObject(
 363                notExpr,
 364                self.Restrictions.Merge(nonzero.Target.Restrictions.Merge(length.Target.Restrictions))
 365            );
 366        }
 367
 368
 369        #endregion
 370
 371        #region Reflective Operations
 372
 373        private static MetaObject/*!*/ MakeDocumentationOperation(OperationAction/*!*/ operation, MetaObject/*!*/[]/*!*/ args) {
 374            BinderState state = BinderState.GetBinderState(operation);
 375
 376            return new MetaObject(
 377                Binders.Get(
 378                    BinderState.GetCodeContext(operation),
 379                    state,
 380                    typeof(string),
 381                    "__doc__",
 382                    args[0].Expression
 383                ),
 384                args[0].Restrictions
 385            );
 386        }
 387
 388        private static MetaObject/*!*/ MakeMemberNamesOperation(OperationAction/*!*/ operation, MetaObject[] args) {
 389            MetaObject self = args[0];
 390            CodeContext context;
 391            if (args.Length > 1 && args[0].LimitType == typeof(CodeContext)) {
 392                self = args[1];
 393                context = (CodeContext)args[0].Value;
 394            } else {
 395                context = BinderState.GetBinderState(operation).Context;
 396            }
 397
 398            if (typeof(IMembersList).IsAssignableFrom(self.LimitType)) {
 399                return BinderState.GetBinderState(operation).Binder.DoOperation(operation.Operation, BinderState.GetCodeContext(operation), args);
 400            }
 401
 402            PythonType pt = DynamicHelpers.GetPythonType(self.Value);
 403            List<string> strNames = GetMemberNames(context, pt, self.Value);
 404
 405            if (pt.IsSystemType) {
 406                return new MetaObject(
 407                    Ast.Constant(strNames),
 408                    Restrictions.InstanceRestriction(self.Expression, self.Value).Merge(self.Restrictions)
 409                );
 410            }
 411
 412            return new MetaObject(
 413                Ast.Constant(strNames),
 414                Restrictions.InstanceRestriction(self.Expression, self.Value).Merge(self.Restrictions)
 415            );
 416        }
 417
 418        internal static MetaObject/*!*/ MakeCallSignatureOperation(MetaObject/*!*/ self, IList<MethodBase/*!*/>/*!*/ targets) {
 419            List<string> arrres = new List<string>();
 420            foreach (MethodBase mb in targets) {
 421                StringBuilder res = new StringBuilder();
 422                string comma = "";
 423
 424                Type retType = CompilerHelpers.GetReturnType(mb);
 425                if (retType != typeof(void)) {
 426                    res.Append(DynamicHelpers.GetPythonTypeFromType(retType).Name);
 427                    res.Append(" ");
 428                }
 429
 430                MethodInfo mi = mb as MethodInfo;
 431                if (mi != null) {
 432                    string name;
 433                    NameConverter.TryGetName(DynamicHelpers.GetPythonTypeFromType(mb.DeclaringType), mi, out name);
 434                    res.Append(name);
 435                } else {
 436                    res.Append(DynamicHelpers.GetPythonTypeFromType(mb.DeclaringType).Name);
 437                }
 438
 439                res.Append("(");
 440                if (!CompilerHelpers.IsStatic(mb)) {
 441                    res.Append("self");
 442                    comma = ", ";
 443                }
 444
 445                foreach (ParameterInfo pi in mb.GetParameters()) {
 446                    if (pi.ParameterType == typeof(CodeContext)) continue;
 447
 448                    res.Append(comma);
 449                    res.Append(DynamicHelpers.GetPythonTypeFromType(pi.ParameterType).Name + " " + pi.Name);
 450                    comma = ", ";
 451                }
 452                res.Append(")");
 453                arrres.Add(res.ToString());
 454            }
 455
 456            return new MetaObject(
 457                Ast.Constant(arrres.ToArray()),
 458                self.Restrictions.Merge(Restrictions.InstanceRestriction(self.Expression, self.Value))
 459            );
 460        }
 461
 462        private static MetaObject/*!*/ MakeIscallableOperation(OperationAction/*!*/ operation, MetaObject/*!*/[]/*!*/ args) {
 463            // Certain non-python types (encountered during interop) are callable, but don't have 
 464            // a __call__ attribute. The default base binder also checks these, but since we're overriding
 465            // the base binder, we check them here.
 466            MetaObject self = args[0];
 467            
 468            // only applies when called from a Python site
 469            if (typeof(Delegate).IsAssignableFrom(self.LimitType) ||
 470                typeof(MethodGroup).IsAssignableFrom(self.LimitType)) {
 471                return new MetaObject(
 472                    Ast.Constant(true),
 473                    self.Restrict(self.LimitType).Restrictions
 474                );
 475            }
 476
 477            BinderState state = BinderState.GetBinderState(operation);
 478            Expression isCallable = Ast.NotEqual(
 479                Binders.TryGet(
 480                    BinderState.GetCodeContext(operation),
 481                    state,
 482                    typeof(object),
 483                    "__call__",
 484                    self.Expression
 485                ),
 486                Ast.Constant(OperationFailed.Value)
 487            );
 488
 489            return new MetaObject(
 490                isCallable,
 491                self.Restrict(self.LimitType).Restrictions
 492            );
 493        }
 494
 495        #endregion
 496
 497        #region Common Binary Operations
 498
 499        private static MetaObject/*!*/ MakeSimpleOperation(MetaObject/*!*/[]/*!*/ types, OperationAction/*!*/ operation) {
 500            RestrictTypes(types);
 501
 502            SlotOrFunction fbinder;
 503            SlotOrFunction rbinder;
 504            PythonTypeSlot fSlot;
 505            PythonTypeSlot rSlot;
 506            GetOpreatorMethods(types, operation.Operation, BinderState.GetBinderState(operation), out fbinder, out rbinder, out fSlot, out rSlot);
 507
 508            return MakeBinaryOperatorResult(types, operation, fbinder, rbinder, fSlot, rSlot);
 509        }
 510
 511        private static void GetOpreatorMethods(MetaObject/*!*/[]/*!*/ types, string oper, BinderState state, out SlotOrFunction fbinder, out SlotOrFunction rbinder, out PythonTypeSlot fSlot, out PythonTypeSlot rSlot) {
 512            oper = NormalizeOperator(oper);
 513            if (IsInPlace(oper)) {
 514                oper = DirectOperation(oper);
 515            }
 516
 517            SymbolId op, rop;
 518            if (!TypeInfo.IsReverseOperator(oper)) {
 519                op = Symbols.OperatorToSymbol(oper);
 520                rop = Symbols.OperatorToReversedSymbol(oper);
 521            } else {
 522                // coming back after coercion, just try reverse operator.
 523                rop = Symbols.OperatorToSymbol(oper);
 524                op = Symbols.OperatorToReversedSymbol(oper);
 525            }
 526
 527            fSlot = null;
 528            rSlot = null;
 529            PythonType fParent, rParent;
 530
 531            if (oper == StandardOperators.Multiply && 
 532                IsSequence(types[0]) && 
 533                !PythonOps.IsNonExtensibleNumericType(types[1].LimitType)) {
 534                // class M:
 535                //      def __rmul__(self, other):
 536                //          print "CALLED"
 537                //          return 1
 538                //
 539                // print [1,2] * M()
 540                //
 541                // in CPython this results in a successful call to __rmul__ on the type ignoring the forward
 542                // multiplication.  But calling the __mul__ method directly does NOT return NotImplemented like
 543                // one might expect.  Therefore we explicitly convert the MetaObject argument into an Index
 544                // for binding purposes.  That allows this to work at multiplication time but not with
 545                // a direct call to __mul__.
 546
 547                MetaObject[] newTypes = new MetaObject[2];
 548                newTypes[0] = types[0];
 549                newTypes[1] = new MetaObject(
 550                    Ast.New(
 551                        typeof(Index).GetConstructor(new Type[] { typeof(object) }),
 552                        Ast.ConvertHelper(types[1].Expression, typeof(object))
 553                    ),
 554                    Restrictions.Empty
 555                );
 556                types = newTypes;
 557            }
 558
 559            if (!SlotOrFunction.TryGetBinder(state, types, op, SymbolId.Empty, out fbinder, out fParent)) {
 560                foreach (PythonType pt in MetaPythonObject.GetPythonType(types[0]).ResolutionOrder) {
 561                    if (pt.TryLookupSlot(state.Context, op, out fSlot)) {
 562                        fParent = pt;
 563                        break;
 564                    }
 565                }
 566            }
 567
 568            if (!SlotOrFunction.TryGetBinder(state, types, SymbolId.Empty, rop, out rbinder, out rParent)) {
 569                foreach (PythonType pt in MetaPythonObject.GetPythonType(types[1]).ResolutionOrder) {
 570                    if (pt.TryLookupSlot(state.Context, rop, out rSlot)) {
 571                        rParent = pt;
 572                        break;
 573                    }
 574                }
 575            }
 576
 577            if (fParent != null && (rbinder.Success || rSlot != null) && rParent != fParent && rParent.IsSubclassOf(fParent)) {
 578                // Python says if x + subx and subx defines __r*__ we should call r*.
 579                fbinder = SlotOrFunction.Empty;
 580                fSlot = null;
 581            }
 582
 583            if (!fbinder.Success && !rbinder.Success && fSlot == null && rSlot == null) {
 584                if (op == Symbols.OperatorTrueDivide || op == Symbols.OperatorReverseTrueDivide) {
 585                    // true div on a type which doesn't support it, go ahead and try normal divide
 586                    string newOp = op == Symbols.OperatorTrueDivide ? StandardOperators.Divide : OperatorStrings.ReverseDivide;
 587
 588                    GetOpreatorMethods(types, newOp, state, out fbinder, out rbinder, out fSlot, out rSlot);
 589                }
 590            }
 591        }
 592
 593        private static bool IsSequence(MetaObject/*!*/ metaObject) {
 594            if (typeof(List).IsAssignableFrom(metaObject.LimitType) ||
 595                typeof(PythonTuple).IsAssignableFrom(metaObject.LimitType) ||
 596                typeof(String).IsAssignableFrom(metaObject.LimitType)) {
 597                return true;
 598            }
 599            return false;
 600        }
 601
 602        private static MetaObject/*!*/ MakeBinaryOperatorResult(MetaObject/*!*/[]/*!*/ types, OperationAction/*!*/ operation, SlotOrFunction/*!*/ fCand, SlotOrFunction/*!*/ rCand, PythonTypeSlot fSlot, PythonTypeSlot rSlot) {
 603            Assert.NotNull(operation, fCand, rCand);
 604
 605            string op = operation.Operation;
 606            SlotOrFunction fTarget, rTarget;
 607
 608            // TODO: some Builder class for condition, body, vars
 609            ConditionalBuilder bodyBuilder = new ConditionalBuilder(operation);
 610
 611            if (IsInPlace(op)) {
 612                // in place operator, see if there's a specific method that handles it.
 613                SlotOrFunction function = SlotOrFunction.GetSlotOrFunction(BinderState.GetBinderState(operation), Symbols.OperatorToSymbol(op), types);
 614
 615                // we don't do a coerce for in place operators if the lhs implements __iop__
 616                if (!MakeOneCompareGeneric(function, false, types, MakeCompareReturn, bodyBuilder)) {
 617                    // the method handles it and always returns a useful value.
 618                    return bodyBuilder.GetMetaObject(types);
 619                }
 620            }
 621
 622            if (!SlotOrFunction.GetCombinedTargets(fCand, rCand, out fTarget, out rTarget) &&
 623                fSlot == null &&
 624                rSlot == null &&
 625                !ShouldCoerce(operation, types[0], types[1], false) &&
 626                !ShouldCoerce(operation, types[1], types[0], false) &&
 627                bodyBuilder.NoConditions) {
 628                return MakeRuleForNoMatch(operation, op, types);
 629            }
 630
 631            if (ShouldCoerce(operation, types[0], types[1], false) && 
 632                (op != StandardOperators.Mod || !MetaPythonObject.GetPythonType(types[0]).IsSubclassOf(TypeCache.String))) {
 633                // need to try __coerce__ first.
 634                DoCoerce(operation, bodyBuilder, op, types, false);
 635            }
 636
 637            if (MakeOneTarget(BinderState.GetBinderState(operation), fTarget, fSlot, bodyBuilder, false, types)) {
 638                if (ShouldCoerce(operation, types[1], types[0], false)) {
 639                    // need to try __coerce__ on the reverse first                    
 640                    DoCoerce(operation, bodyBuilder, op, new MetaObject[] { types[1], types[0] }, true);
 641                }
 642
 643                if (rSlot != null) {
 644                    MakeSlotCall(BinderState.GetBinderState(operation), types, bodyBuilder, rSlot, true);
 645                    bodyBuilder.FinishCondition(MakeBinaryThrow(operation, op, types).Expression);
 646                } else if (MakeOneTarget(BinderState.GetBinderState(operation), rTarget, rSlot, bodyBuilder, false, types)) {
 647                    // need to fallback to throwing or coercion
 648                    bodyBuilder.FinishCondition(MakeBinaryThrow(operation, op, types).Expression);
 649                }
 650            }
 651
 652            return bodyBuilder.GetMetaObject(types);
 653        }
 654
 655        private static void MakeCompareReturn(ConditionalBuilder/*!*/ bodyBuilder, Expression retCondition, Expression/*!*/ retValue, bool isReverse) {
 656            if (retCondition != null) {
 657                bodyBuilder.AddCondition(retCondition, retValue);
 658            } else {
 659                bodyBuilder.FinishCondition(retValue);
 660            }
 661        }
 662
 663        /// <summary>
 664        /// Delegate for finishing the comparison.   This takes in a condition and a return value and needs to update the ConditionalBuilder
 665        /// with the appropriate resulting body.  The condition may be null.
 666        /// </summary>
 667        private delegate void ComparisonHelper(ConditionalBuilder/*!*/ bodyBuilder, Expression retCondition, Expression/*!*/ retValue, bool isReverse);
 668
 669        /// <summary>
 670        /// Helper to handle a comparison operator call.  Checks to see if the call can
 671        /// return NotImplemented and allows the caller to modify the expression that
 672        /// is ultimately returned (e.g. to turn __cmp__ into a bool after a comparison)
 673        /// </summary>
 674        private static bool MakeOneCompareGeneric(SlotOrFunction/*!*/ target, bool reverse, MetaObject/*!*/[]/*!*/ types, ComparisonHelper returner, ConditionalBuilder/*!*/ bodyBuilder) {
 675            if (target == SlotOrFunction.Empty || !target.Success) return true;
 676
 677            ParameterExpression tmp;
 678
 679            if (target.ReturnType == typeof(bool)) {
 680                tmp = bodyBuilder.CompareRetBool;
 681            } else {
 682                tmp = Ast.Variable(target.ReturnType, "compareRetValue");
 683                bodyBuilder.AddVariable(tmp);
 684            }
 685
 686            if (target.MaybeNotImplemented) {
 687                Expression call = target.Target.Expression;
 688                Expression assign = Ast.Assign(tmp, call);
 689
 690                returner(
 691                    bodyBuilder,
 692                    Ast.NotEqual(
 693                        assign,
 694                        Ast.Constant(PythonOps.NotImplemented)
 695                    ),
 696                    tmp,
 697                    reverse);
 698                return true;
 699            } else {
 700                returner(
 701                    bodyBuilder,
 702                    null,
 703                    target.Target.Expression,
 704                    reverse
 705                );
 706                return false;
 707            }
 708        }
 709
 710        private static bool MakeOneTarget(BinderState/*!*/ state, SlotOrFunction/*!*/ target, PythonTypeSlot slotTarget, ConditionalBuilder/*!*/ bodyBuilder, bool reverse, MetaObject/*!*/[]/*!*/ types) {
 711            if (target == SlotOrFunction.Empty && slotTarget == null) return true;
 712
 713            if (slotTarget != null) {
 714                MakeSlotCall(state, types, bodyBuilder, slotTarget, reverse);
 715                return true;
 716            } else if (target.MaybeNotImplemented) {
 717                Debug.Assert(target.ReturnType == typeof(object));
 718
 719                ParameterExpression tmp = Ast.Variable(typeof(object), "slot");
 720                bodyBuilder.AddVariable(tmp);
 721
 722                bodyBuilder.AddCondition(
 723                    Ast.NotEqual(
 724                        Ast.Assign(
 725                            tmp,
 726                            target.Target.Expression
 727                        ),
 728                        Ast.Property(null, typeof(PythonOps).GetProperty("NotImplemented"))
 729                    ),
 730                    tmp
 731                );
 732
 733                return true;
 734            } else {
 735                bodyBuilder.FinishCondition(target.Target.Expression);
 736                return false;
 737            }
 738        }
 739
 740        private static void MakeSlotCall(BinderState/*!*/ state, MetaObject/*!*/[]/*!*/ types, ConditionalBuilder/*!*/ bodyBuilder, PythonTypeSlot/*!*/ slotTarget, bool reverse) {
 741            Debug.Assert(slotTarget != null);
 742
 743            Expression self, other;
 744            if (reverse) {
 745                self = types[1].Expression;
 746                other = types[0].Expression;
 747            } else {
 748                self = types[0].Expression;
 749                other = types[1].Expression;
 750            }
 751
 752            MakeSlotCallWorker(state, slotTarget, self, bodyBuilder, other);
 753        }
 754
 755        private static void MakeSlotCallWorker(BinderState/*!*/ state, PythonTypeSlot/*!*/ slotTarget, Expression/*!*/ self, ConditionalBuilder/*!*/ bodyBuilder, params Expression/*!*/[]/*!*/ args) {
 756            // Generate:
 757            // 
 758            // SlotTryGetValue(context, slot, selfType, out callable) && (tmp=callable(args)) != NotImplemented) ?
 759            //      tmp :
 760            //      RestOfOperation
 761            //
 762            ParameterExpression callable = Ast.Variable(typeof(object), "slot");
 763            ParameterExpression tmp = Ast.Variable(typeof(object), "slot");
 764
 765            bodyBuilder.AddCondition(
 766                Ast.AndAlso(
 767                    Ast.Call(
 768                        typeof(PythonOps).GetMethod("SlotTryGetValue"),
 769                        Ast.Constant(state.Context),
 770                        Ast.ConvertHelper(Utils.WeakConstant(slotTarget), typeof(PythonTypeSlot)),
 771                        Ast.ConvertHelper(self, typeof(object)),
 772                        Ast.Call(
 773                            typeof(DynamicHelpers).GetMethod("GetPythonType"),
 774                            Ast.ConvertHelper(self, typeof(object))
 775                        ),
 776                        callable
 777                    ),
 778                    Ast.NotEqual(
 779                        Ast.Assign(
 780                            tmp,
 781                            Ast.Dynamic(
 782                                new InvokeBinder(
 783                                    state,
 784                                    new CallSignature(args.Length)
 785                                ),
 786                                typeof(object),
 787                                ArrayUtils.Insert(Ast.Constant(state.Context), (Expression)callable, args)
 788                            )
 789                        ),
 790                        Ast.Property(null, typeof(PythonOps).GetProperty("NotImplemented"))
 791                    )
 792                ),
 793                tmp
 794            );
 795            bodyBuilder.AddVariable(callable);
 796            bodyBuilder.AddVariable(tmp);
 797        }
 798
 799        private static void DoCoerce(OperationAction/*!*/ operation, ConditionalBuilder/*!*/ bodyBuilder, string op, MetaObject/*!*/[]/*!*/ types, bool reverse) {
 800            DoCoerce(operation, bodyBuilder, op, types, reverse, delegate(Expression e) {
 801                return e;
 802            });
 803        }
 804
 805        /// <summary>
 806        /// calls __coerce__ for old-style classes and performs the operation if the coercion is successful.
 807        /// </summary>
 808        private static void DoCoerce(OperationAction/*!*/ operation, ConditionalBuilder/*!*/ bodyBuilder, string op, MetaObject/*!*/[]/*!*/ types, bool reverse, Func<Expression, Expression> returnTransform) {
 809            ParameterExpression coerceResult = Ast.Variable(typeof(object), "coerceResult");
 810            ParameterExpression coerceTuple = Ast.Variable(typeof(PythonTuple), "coerceTuple");
 811
 812            if (!bodyBuilder.TestCoercionRecursionCheck) {
 813                // during coercion we need to enforce recursion limits if
 814                // they're enabled and the rule's test needs to reflect this.                
 815                bodyBuilder.Restrictions = bodyBuilder.Restrictions.Merge(
 816                    Restrictions.ExpressionRestriction(
 817                        Ast.Equal(
 818                            Ast.Call(typeof(PythonOps).GetMethod("ShouldEnforceRecursion")),
 819                            Ast.Constant(PythonFunction.EnforceRecursion)
 820                        )
 821                    )
 822                );
 823
 824                bodyBuilder.TestCoercionRecursionCheck = true;
 825            }
 826
 827            // tmp = self.__coerce__(other)
 828            // if tmp != null && tmp != NotImplemented && (tuple = PythonOps.ValidateCoerceResult(tmp)) != null:
 829            //      return operation(tuple[0], tuple[1])                        
 830            SlotOrFunction slot = SlotOrFunction.GetSlotOrFunction(BinderState.GetBinderState(operation), Symbols.Coerce, types);
 831
 832            if (slot.Success) {
 833                bodyBuilder.AddCondition(
 834                    Ast.AndAlso(
 835                        Ast.Not(
 836                            Ast.TypeIs(
 837                                Ast.Assign(
 838                                    coerceResult,
 839                                    slot.Target.Expression
 840                                ),
 841                                typeof(OldInstance)
 842                            )
 843                        ),
 844                        Ast.NotEqual(
 845                            Ast.Assign(
 846                                coerceTuple,
 847                                Ast.Call(
 848                                    typeof(PythonOps).GetMethod("ValidateCoerceResult"),
 849                                    coerceResult
 850                                )
 851                            ),
 852                            Ast.Constant(null)
 853                        )
 854                    ),
 855                    BindingHelpers.AddRecursionCheck(
 856                        returnTransform(
 857                            Ast.Dynamic(
 858                                new OperationBinder(
 859                                    BinderState.GetBinderState(operation),
 860                                    DisallowCoerce + op
 861                                ),
 862                                typeof(object),
 863                                reverse ? CoerceTwo(coerceTuple) : CoerceOne(coerceTuple),
 864                                reverse ? CoerceOne(coerceTuple) : CoerceTwo(coerceTuple)
 865                            )
 866                        )
 867                    )
 868                );
 869                bodyBuilder.AddVariable(coerceResult);
 870                bodyBuilder.AddVariable(coerceTuple);
 871            }
 872        }
 873
 874        private static MethodCallExpression/*!*/ CoerceTwo(ParameterExpression/*!*/ coerceTuple) {
 875            return Ast.Call(
 876                typeof(PythonOps).GetMethod("GetCoerceResultTwo"),
 877                coerceTuple
 878            );
 879        }
 880
 881        private static MethodCallExpression/*!*/ CoerceOne(ParameterExpression/*!*/ coerceTuple) {
 882            return Ast.Call(
 883                typeof(PythonOps).GetMethod("GetCoerceResultOne"),
 884                coerceTuple
 885            );
 886        }
 887
 888
 889        #endregion
 890
 891        #region Comparison Operations
 892
 893        private static MetaObject/*!*/ MakeComparisonOperation(MetaObject/*!*/[]/*!*/ types, OperationAction/*!*/ operation) {
 894            RestrictTypes(types);
 895
 896            string op = NormalizeOperator(operation.Operation);
 897            if (op == StandardOperators.Compare) {
 898                return MakeSortComparisonRule(types, operation);
 899            }
 900
 901            BinderState state = BinderState.GetBinderState(operation);
 902            Debug.Assert(types.Length == 2);
 903            MetaObject xType = types[0], yType = types[1];
 904            SymbolId opSym = Symbols.OperatorToSymbol(op);
 905            SymbolId ropSym = Symbols.OperatorToReversedSymbol(op);
 906            // reverse
 907            MetaObject[] rTypes = new MetaObject[] { types[1], types[0] };
 908
 909            SlotOrFunction fop, rop, cmp, rcmp;
 910            fop = SlotOrFunction.GetSlotOrFunction(state, opSym, types);
 911            rop = SlotOrFunction.GetSlotOrFunction(state, ropSym, rTypes);
 912            cmp = SlotOrFunction.GetSlotOrFunction(state, Symbols.Cmp, types);
 913            rcmp = SlotOrFunction.GetSlotOrFunction(state, Symbols.Cmp, rTypes);
 914
 915            ConditionalBuilder bodyBuilder = new ConditionalBuilder(operation);
 916
 917            SlotOrFunction.GetCombinedTargets(fop, rop, out fop, out rop);
 918            SlotOrFunction.GetCombinedTargets(cmp, rcmp, out cmp, out rcmp);
 919
 920            // first try __op__ or __rop__ and return the value
 921            if (MakeOneCompareGeneric(fop, false, types, MakeCompareReturn, bodyBuilder)) {
 922                if (MakeOneCompareGeneric(rop, true, types, MakeCompareReturn, bodyBuilder)) {
 923
 924                    // then try __cmp__ or __rcmp__ and compare the resulting int appropriaetly
 925                    if (ShouldCoerce(operation, xType, yType, true)) {
 926                        DoCoerce(operation, bodyBuilder, StandardOperators.Compare, types, false, delegate(Expression e) {
 927                            return GetCompareTest(op, e, false);
 928                        });
 929                    }
 930
 931                    if (MakeOneCompareGeneric(
 932                        cmp,
 933                        false,
 934                        types,
 935                        delegate(ConditionalBuilder builder, Expression retCond, Expression expr, bool reverse) {
 936                            MakeCompareTest(op, builder, retCond, expr, reverse);
 937                        },
 938                        bodyBuilder)) {
 939
 940                        if (ShouldCoerce(operation, yType, xType, true)) {
 941                            DoCoerce(operation, bodyBuilder, StandardOperators.Compare, rTypes, true, delegate(Expression e) {
 942                                return GetCompareTest(op, e, true);
 943                            });
 944                        }
 945
 946                        if (MakeOneCompareGeneric(
 947                            rcmp,
 948                            true,
 949                            types,
 950                            delegate(ConditionalBuilder builder, Expression retCond, Expression expr, bool reverse) {
 951                                MakeCompareTest(op, builder, retCond, expr, reverse);
 952                            },
 953                            bodyBuilder)) {
 954                            bodyBuilder.FinishCondition(MakeFallbackCompare(op, types));
 955                        }
 956                    }
 957                }
 958            }
 959
 960            return bodyBuilder.GetMetaObject(types);
 961        }
 962
 963        /// <summary>
 964        /// Makes the comparison rule which returns an int (-1, 0, 1).  TODO: Better name?
 965        /// </summary>
 966        private static MetaObject/*!*/ MakeSortComparisonRule(MetaObject/*!*/[]/*!*/ types, OperationAction/*!*/ operation) {
 967            MetaObject fastPath = FastPathCompare(types);
 968            if (fastPath != null) {
 969                return fastPath;
 970            }
 971
 972            string op = operation.Operation;
 973
 974            // Python compare semantics: 
 975            //      if the types are the same invoke __cmp__ first.
 976            //      If __cmp__ is not defined or the types are different:
 977            //          try rich comparisons (eq, lt, gt, etc...) 
 978            //      If the types are not the same and rich cmp didn't work finally try __cmp__
 979            //      If __cmp__ isn't defined return a comparison based upon the types.
 980            //
 981            // Along the way we try both forward and reverse versions (try types[0] and then
 982            // try types[1] reverse version).  For these comparisons __cmp__ and __eq__ are their
 983            // own reversals and __gt__ is the opposite of __lt__.
 984
 985            // collect all the comparison methods, most likely we won't need them all.
 986            MetaObject[] rTypes = new MetaObject[] { types[1], types[0] };
 987            SlotOrFunction cfunc, rcfunc, eqfunc, reqfunc, ltfunc, gtfunc, rltfunc, rgtfunc;
 988
 989            BinderState state = BinderState.GetBinderState(operation);
 990            cfunc = SlotOrFunction.GetSlotOrFunction(state, Symbols.Cmp, types);
 991            rcfunc = SlotOrFunction.GetSlotOrFunction(state, Symbols.Cmp, rTypes);
 992            eqfunc = SlotOrFunction.GetSlotOrFunction(state, Symbols.OperatorEquals, types);
 993            reqfunc = SlotOrFunction.GetSlotOrFunction(state, Symbols.OperatorEquals, rTypes);
 994            ltfunc = SlotOrFunction.GetSlotOrFunction(state, Symbols.OperatorLessThan, types);
 995            gtfunc = SlotOrFunction.GetSlotOrFunction(state, Symbols.OperatorGreaterThan, types);
 996            rltfunc = SlotOrFunction.GetSlotOrFunction(state, Symbols.OperatorLessThan, rTypes);
 997            rgtfunc = SlotOrFunction.GetSlotOrFunction(state, Symbols.OperatorGreaterThan, rTypes);
 998
 999            // inspect forward and reverse versions so we can pick one or both.
1000            SlotOrFunction cTarget, rcTarget, eqTarget, reqTarget, ltTarget, rgtTarget, gtTarget, rltTarget;
1001            SlotOrFunction.GetCombinedTargets(cfunc, rcfunc, out cTarget, out rcTarget);
1002            SlotOrFunction.GetCombinedTargets(eqfunc, reqfunc, out eqTarget, out reqTarget);
1003            SlotOrFunction.GetCombinedTargets(ltfunc, rgtfunc, out ltTarget, out rgtTarget);
1004            SlotOrFunction.GetCombinedTargets(gtfunc, rltfunc, out gtTarget, out rltTarget);
1005
1006            PythonType xType = MetaPythonObject.GetPythonType(types[0]);
1007            PythonType yType = MetaPythonObject.GetPythonType(types[1]);
1008
1009            // now build the rule from the targets.
1010            // bail if we're comparing to null and the rhs can't do anything special...
1011            if (xType.IsNull) {
1012                if (yType.IsNull) {
1013                    return new MetaObject(
1014                        Ast.Zero(),
1015                        Restrictions.Combine(types)
1016                    );
1017                } else if (yType.UnderlyingSystemType.IsPrimitive || yType.UnderlyingSystemType == typeof(Microsoft.Scripting.Math.BigInteger)) {
1018                    return new MetaObject(
1019                        Ast.Constant(-1),
1020                        Restrictions.Combine(types)
1021                    );
1022                }
1023            }
1024
1025            ConditionalBuilder bodyBuilder = new ConditionalBuilder(operation);
1026
1027            bool tryRich = true, more = true;
1028            if (xType == yType && cTarget != SlotOrFunction.Empty) {
1029                // if the types are equal try __cmp__ first
1030                if (ShouldCoerce(operation, types[0], types[1], true)) {
1031                    // need to try __coerce__ first.
1032                    DoCoerce(operation, bodyBuilder, StandardOperators.Compare, types, false);
1033                }
1034
1035                more = more && MakeOneCompareGeneric(cTarget, false, types, MakeCompareReverse, bodyBuilder);
1036
1037                if (xType != TypeCache.OldInstance) {
1038                    // try __cmp__ backwards for new-style classes and don't fallback to
1039                    // rich comparisons if available
1040                    more = more && MakeOneCompareGeneric(rcTarget, true, types, MakeCompareReverse, bodyBuilder);
1041                    tryRich = false;
1042                }
1043            }
1044
1045            if (tryRich && more) {
1046                // try the >, <, ==, !=, >=, <=.  These don't get short circuited using the more logic
1047                // because they don't give a definitive answer even if they return bool.  Only if they
1048                // return true do we know to return 0, -1, or 1.
1049                // try eq
1050                MakeOneCompareGeneric(eqTarget, false, types, MakeCompareToZero, bodyBuilder);
1051                MakeOneCompareGeneric(reqTarget, true, types, MakeCompareToZero, bodyBuilder);
1052
1053                // try less than & reveā€¦

Large files files are truncated, but you can click here to view the full file