PageRenderTime 215ms CodeModel.GetById 80ms app.highlight 95ms RepoModel.GetById 26ms app.codeStats 1ms

/Microsoft.Scripting/Actions/Calls/MethodBinder.cs

https://bitbucket.org/stefanrusek/xronos
C# | 1028 lines | 707 code | 143 blank | 178 comment | 210 complexity | 1cb83d74c67c2fa838c5634ec8b62bd4 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
  16#if CODEPLEX_40
  17using System;
  18#else
  19using System; using Microsoft;
  20#endif
  21using System.Collections.Generic;
  22using System.Diagnostics;
  23using System.Reflection;
  24using System.Runtime.CompilerServices;
  25#if !CODEPLEX_40
  26using Microsoft.Runtime.CompilerServices;
  27#endif
  28
  29#if CODEPLEX_40
  30using System.Dynamic;
  31#else
  32using Microsoft.Scripting;
  33#endif
  34using Microsoft.Contracts;
  35using Microsoft.Scripting.Actions;
  36using Microsoft.Scripting.Runtime;
  37using Microsoft.Scripting.Utils;
  38using Microsoft.Scripting.Generation;
  39
  40namespace Microsoft.Scripting.Actions.Calls {
  41    /// <summary>
  42    /// Provides binding and overload resolution to .NET methods.
  43    /// 
  44    /// MethodBinder's can be used for:
  45    ///     generating new AST code for calling a method 
  46    ///     calling a method via reflection at runtime
  47    ///     (not implemented) performing an abstract call
  48    ///     
  49    /// MethodBinder's support default arguments, optional arguments, by-ref (in and out), and keyword arguments.
  50    /// 
  51    /// Implementation Details:
  52    /// 
  53    /// The MethodBinder works by building up a TargetSet for each number of effective arguments that can be
  54    /// passed to a set of overloads.  For example a set of overloads such as:
  55    ///     foo(object a, object b, object c)
  56    ///     foo(int a, int b)
  57    ///     
  58    /// would have 2 target sets - one for 3 parameters and one for 2 parameters.  For parameter arrays
  59    /// we fallback and create the appropriately sized TargetSet on demand.
  60    /// 
  61    /// Each TargetSet consists of a set of MethodCandidate's.  Each MethodCandidate knows the flattened
  62    /// parameters that could be received.  For example for a function such as:
  63    ///     foo(params int[] args)
  64    ///     
  65    /// When this method is in a TargetSet of size 3 the MethodCandidate takes 3 parameters - all of them
  66    /// ints; if it's in a TargetSet of size 4 it takes 4 parameters.  Effectively a MethodCandidate is 
  67    /// a simplified view that allows all arguments to be treated as required positional arguments.
  68    /// 
  69    /// Each MethodCandidate in turn refers to a MethodTarget.  The MethodTarget is composed of a set
  70    /// of ArgBuilder's and a ReturnBuilder which know how to consume the positional arguments and pass
  71    /// them to the appropriate argument of the destination method.  This includes routing keyword
  72    /// arguments to the correct position, providing the default values for optional arguments, etc...
  73    /// 
  74    /// After binding is finished the MethodCandidates are thrown away and a BindingTarget is returned. 
  75    /// The BindingTarget indicates whether the binding was successful and if not any additional information
  76    /// that should be reported to the user about the failed binding.  It also exposes the MethodTarget which
  77    /// allows consumers to get the flattened list of required parameters for the call.  MethodCandidates
  78    /// are not exposed and are an internal implementation detail of the MethodBinder.
  79    /// </summary>
  80    public sealed class MethodBinder {
  81        private readonly string _name;                           // the name of the method (possibly language specific name which isn't the same as the method base)
  82        private readonly Dictionary<int, TargetSet> _targetSets; // the methods as they map from # of arguments -> the possible TargetSet's.
  83        private readonly string[] _kwArgs;                     // the names of the keyword arguments being provided
  84        private readonly NarrowingLevel _minLevel, _maxLevel;         // specifies the minimum and maximum narrowing levels for conversions during binding
  85        internal readonly DefaultBinder _binder;                      // the ActionBinder which is being used for conversions
  86        private List<MethodCandidate> _paramsCandidates;              // the methods which are params methods which need special treatment because they don't have fixed # of args
  87
  88        #region Constructors
  89
  90        private MethodBinder(ActionBinder binder, string name, IList<MethodBase> methods, string[] kwArgs, NarrowingLevel minLevel, NarrowingLevel maxLevel) {
  91            ContractUtils.RequiresNotNull(binder, "binder");
  92            ContractUtils.RequiresNotNull(name, "name");
  93            ContractUtils.RequiresNotNullItems(methods, "methods");
  94            ContractUtils.RequiresNotNullItems(kwArgs, "kwArgs");
  95
  96            _binder = binder as DefaultBinder;
  97            if (_binder == null) {
  98                throw new InvalidOperationException("MethodBinder requires an instance of DefaultBinder");
  99            }
 100            _name = name;
 101            _kwArgs = kwArgs;
 102            _targetSets = new Dictionary<int, TargetSet>(methods.Count);
 103            _minLevel = minLevel;
 104            _maxLevel = maxLevel;
 105
 106            foreach (MethodBase method in methods) {
 107                if (IsUnsupported(method)) continue;
 108
 109                AddBasicMethodTargets(binder, method);
 110            }
 111
 112            if (_paramsCandidates != null) {
 113                // For all the methods that take a params array, create MethodCandidates that clash with the 
 114                // other overloads of the method
 115                foreach (MethodCandidate maker in _paramsCandidates) {
 116                    foreach (int count in _targetSets.Keys) {
 117                        MethodCandidate target = maker.MakeParamsExtended(binder, count, _kwArgs);
 118                        if (target != null) AddTarget(target);
 119                    }
 120                }
 121            }
 122        }
 123
 124        #endregion
 125
 126        #region Public APIs
 127
 128        /// <summary>
 129        /// Creates a new MethodBinder for binding to the specified methods that will attempt to bind
 130        /// at all defined NarrowingLevels.
 131        /// 
 132        /// The provided ActionBinder is used for determining overload resolution.
 133        /// </summary>
 134        public static MethodBinder MakeBinder(ActionBinder binder, string name, IList<MethodBase> mis) {
 135            return new MethodBinder(binder, name, mis, ArrayUtils.EmptyStrings, NarrowingLevel.None, NarrowingLevel.All);
 136        }
 137
 138        /// <summary>
 139        /// Creates a new MethodBinder for binding to the specified methods on a call which includes keyword arguments that
 140        /// will attempt to bind at all defined NarrowingLevels.
 141        /// 
 142        /// The provided ActionBinder is used for determining overload resolution.
 143        /// </summary>
 144        public static MethodBinder MakeBinder(ActionBinder binder, string name, IList<MethodBase> mis, string[] keywordArgs) {
 145            return new MethodBinder(binder, name, mis, keywordArgs, NarrowingLevel.None, NarrowingLevel.All);
 146        }
 147
 148        /// <summary>
 149        /// Creates a new MethodBinder for binding to the specified methods this will attempt to bind at 
 150        /// the specified NarrowingLevels.
 151        /// 
 152        /// The provided ActionBinder is used for determining overload resolution.
 153        /// </summary>
 154        public static MethodBinder MakeBinder(ActionBinder binder, string name, IList<MethodBase> mis, NarrowingLevel minLevel, NarrowingLevel maxLevel) {
 155            return new MethodBinder(binder, name, mis, ArrayUtils.EmptyStrings, minLevel, maxLevel);
 156        }
 157
 158        /// <summary>
 159        /// Creates a new MethodBinder for binding to the specified methods on a call which includes keyword arguments that
 160        /// will attempt to bind at the specified NarrowingLevels.
 161        /// 
 162        /// The provided ActionBinder is used for determining overload resolution.
 163        /// </summary>
 164        public static MethodBinder MakeBinder(ActionBinder binder, string name, IList<MethodBase> mis, string[] keywordArgs, NarrowingLevel minLevel, NarrowingLevel maxLevel) {
 165            return new MethodBinder(binder, name, mis, keywordArgs, minLevel, maxLevel);
 166        }
 167
 168        /// <summary>
 169        /// Creates a BindingTarget given the specified CallType and parameter types.
 170        /// 
 171        /// The BindingTarget can then be tested for the success or particular type of
 172        /// failure that prevents the method from being called.  The BindingTarget can
 173        /// also be called reflectively at runtime, create an Expression for embedding in
 174        /// a RuleBuilder, or be used for performing an abstract call.
 175        /// 
 176        /// OBSOLETE
 177        /// </summary>
 178        public BindingTarget MakeBindingTarget(CallTypes callType, Type[] types) {
 179            ContractUtils.RequiresNotNull(types, "types");
 180            ContractUtils.RequiresNotNullItems(types, "types");
 181
 182            TargetSet ts = GetTargetSet(types.Length);
 183            if (ts != null && !ts.IsParamsDictionaryOnly()) {
 184                return ts.MakeBindingTarget(callType, types, _kwArgs, _minLevel, _maxLevel);
 185            }
 186
 187            // no target set is applicable, report an error and the expected # of arguments.
 188            int[] expectedArgs = new int[_targetSets.Count + (_paramsCandidates != null ? 1 : 0)];
 189            int i = 0;
 190            foreach (KeyValuePair<int, TargetSet> kvp in _targetSets) {
 191                int count = kvp.Key;
 192
 193                foreach (MethodCandidate cand in kvp.Value._targets) {
 194                    foreach (var x in cand.Parameters) {
 195                        if (x.IsParamsArray || x.IsParamsDict) {
 196                            count--;
 197                        }
 198                    }
 199                }
 200
 201                if (callType == CallTypes.ImplicitInstance) {
 202                    foreach (MethodCandidate cand in kvp.Value._targets) {
 203                        if (IsInstanceMethod(cand)) {
 204                            // dispatch includes an instance method, bump
 205                            // one parameter off.
 206                            count--;
 207                            break;
 208                        }
 209                    }                    
 210                }
 211
 212                
 213                expectedArgs[i++] = count;
 214            }
 215            if (_paramsCandidates != null) {
 216                expectedArgs[expectedArgs.Length - 1] = Int32.MaxValue;
 217            }
 218
 219            return new BindingTarget(Name, callType == CallTypes.None ? types.Length : types.Length - 1, expectedArgs);
 220        }
 221
 222        /// <summary>
 223        /// Creates a BindingTarget given the specified CallType and argument meta-objects.
 224        /// 
 225        /// The BindingTarget can then be tested for the success or particular type of
 226        /// failure that prevents the method from being called. If successfully bound the BindingTarget
 227        /// contains a list of argument meta-objects with additional restrictions that ensure the selection
 228        /// of the particular overload.
 229        /// </summary>
 230        public BindingTarget MakeBindingTarget(CallTypes callType, DynamicMetaObject[] metaObjects) {
 231            ContractUtils.RequiresNotNull(metaObjects, "metaObjects");
 232            ContractUtils.RequiresNotNullItems(metaObjects, "metaObjects");
 233
 234            TargetSet ts = GetTargetSet(metaObjects.Length);
 235            if (ts != null && !ts.IsParamsDictionaryOnly()) {
 236                return ts.MakeBindingTarget(callType, metaObjects, _kwArgs, _minLevel, _maxLevel);
 237            }
 238
 239            // no target set is applicable, report an error and the expected # of arguments.
 240            int[] expectedArgs = new int[_targetSets.Count + (_paramsCandidates != null ? 1 : 0)];
 241            int i = 0;
 242            foreach (KeyValuePair<int, TargetSet> kvp in _targetSets) {
 243                int count = kvp.Key;
 244                foreach (MethodCandidate cand in kvp.Value._targets) {
 245                    foreach (var x in cand.Parameters) {
 246                        if (x.IsParamsArray || x.IsParamsDict) {
 247                            count--;
 248                        }
 249                    }
 250                }
 251
 252                if (callType == CallTypes.ImplicitInstance) {
 253                    foreach (MethodCandidate cand in kvp.Value._targets) {
 254                        if (IsInstanceMethod(cand)) {
 255                            // dispatch includes an instance method, bump
 256                            // one parameter off.
 257                            count--;
 258                            break;
 259                        }
 260
 261                    }
 262                }
 263                expectedArgs[i++] = count;
 264            }
 265            if (_paramsCandidates != null) {
 266                expectedArgs[expectedArgs.Length - 1] = Int32.MaxValue;
 267            }
 268
 269            return new BindingTarget(Name, callType == CallTypes.None ? metaObjects.Length : metaObjects.Length - 1, expectedArgs);
 270        }
 271
 272        private static bool IsInstanceMethod(MethodCandidate cand) {
 273            return !CompilerHelpers.IsStatic(cand.Target.Method) ||
 274                                        (cand.Target.Method.IsDefined(typeof(ExtensionAttribute), false));
 275        }
 276
 277        /// <summary>
 278        /// Gets the name of the MethodBinder as provided at construction time.
 279        /// 
 280        /// The name may differ from the name of the underlying method bases if the
 281        /// language provides some mapping from .NET method names to language specific
 282        /// method names.  It is flowed through the MethodBinder primarily for error
 283        /// reporting purposes.
 284        /// </summary>
 285        public string Name {
 286            get {
 287                return _name;
 288            }
 289        }
 290
 291        [Confined]
 292        public override string ToString() {
 293            string res = "";
 294            foreach (TargetSet ts in _targetSets.Values) {
 295                res += ts + Environment.NewLine;
 296            }
 297            return res;
 298        }
 299
 300        #endregion
 301
 302        #region TargetSet construction
 303
 304        private TargetSet GetTargetSet(int nargs) {
 305            TargetSet ts;
 306
 307            // see if we've precomputed the TargetSet...
 308            if (_targetSets.TryGetValue(nargs, out ts)) {
 309                return ts;
 310            } else if (_paramsCandidates != null) {
 311                // build a new target set specific to the number of
 312                // arguments we have
 313                ts = BuildTargetSet(nargs);
 314                if (ts._targets.Count > 0) {
 315                    return ts;
 316                }
 317            }
 318
 319            return null;
 320        }
 321
 322        private TargetSet BuildTargetSet(int count) {
 323            TargetSet ts = new TargetSet(this, count);
 324            if (_paramsCandidates != null) {
 325                foreach (MethodCandidate maker in _paramsCandidates) {
 326                    MethodCandidate target = maker.MakeParamsExtended(_binder, count, _kwArgs);
 327                    if (target != null) ts.Add(target);
 328                }
 329            }
 330
 331            return ts;
 332        }
 333
 334        private void AddTarget(MethodCandidate target) {
 335            int count = target.Target.ParameterCount;
 336            TargetSet set;
 337            if (!_targetSets.TryGetValue(count, out set)) {
 338                set = new TargetSet(this, count);
 339                _targetSets[count] = set;
 340            }
 341            set.Add(target);
 342        }
 343
 344        private void AddSimpleTarget(MethodCandidate target) {
 345            AddTarget(target);
 346            if (BinderHelpers.IsParamsMethod(target.Target.Method)) {
 347                if (_paramsCandidates == null) _paramsCandidates = new List<MethodCandidate>();
 348                _paramsCandidates.Add(target);
 349            }
 350        }
 351
 352        private static ArgBuilder MakeInstanceBuilder(ActionBinder binder, MethodBase method, List<ParameterWrapper> parameters, ref int argIndex) {
 353            if (!CompilerHelpers.IsStatic(method)) {
 354                parameters.Add(new ParameterWrapper(binder, method.DeclaringType, null, true));
 355                return new SimpleArgBuilder(method.DeclaringType, argIndex++, false, false);
 356            } else {
 357                return new NullArgBuilder();
 358            }
 359        }
 360
 361        private void AddBasicMethodTargets(ActionBinder binder, MethodBase method) {
 362            Assert.NotNull(binder, method);
 363
 364            var parameterInfos = method.GetParameters();
 365            var parameters = new List<ParameterWrapper>();
 366            var arguments = new List<ArgBuilder>(parameterInfos.Length);
 367            var defaultArguments = new List<ArgBuilder>();
 368            int argIndex = 0;
 369            var instanceBuilder = MakeInstanceBuilder(binder, method, parameters, ref argIndex);
 370
 371            bool hasByRefOrOut = false;
 372            bool hasDefaults = false;
 373
 374            var infoIndex = binder.PrepareParametersBinding(parameterInfos, arguments, parameters, ref argIndex);
 375            for (; infoIndex < parameterInfos.Length; infoIndex++) {
 376                var pi = parameterInfos[infoIndex];
 377
 378                if (binder.BindSpecialParameter(pi, arguments, parameters, ref argIndex)) {
 379                    continue;
 380                }
 381
 382                int indexForArgBuilder, kwIndex = GetKeywordIndex(pi);
 383                if (kwIndex == ParameterNotPassedByKeyword) {
 384                    // positional argument, we simply consume the next argument
 385                    indexForArgBuilder = argIndex++;
 386                } else {
 387                    // keyword argument, we just tell the simple arg builder to consume arg 0.
 388                    // KeywordArgBuilder will then pass in the correct single argument based 
 389                    // upon the actual argument number provided by the user.
 390                    indexForArgBuilder = 0;
 391                }
 392
 393                // if the parameter is default we need to build a default arg builder and then
 394                // build a reduced method at the end.  
 395                if (!CompilerHelpers.IsMandatoryParameter(pi)) {
 396                    // We need to build the default builder even if we have a parameter for it already to
 397                    // get good consistency of our error messages.  But consider a method like 
 398                    // def foo(a=1, b=2) and the user calls it as foo(b=3). Then adding the default
 399                    // value breaks an otherwise valid call.  This is because we only generate MethodCandidates
 400                    // filling in the defaults from right to left (so the method - 1 arg requires a,
 401                    // and the method minus 2 args requires b).  So we only add the default if it's 
 402                    // a positional arg or we don't already have a default value.
 403                    if (kwIndex == -1 || !hasDefaults) {
 404                        defaultArguments.Add(new DefaultArgBuilder(pi));
 405                        hasDefaults = true;
 406                    } else {
 407                        defaultArguments.Add(null);
 408                    }
 409                } else if (defaultArguments.Count > 0) {
 410                    // non-contigious default parameter
 411                    defaultArguments.Add(null);
 412                }
 413
 414                ArgBuilder ab;
 415                if (pi.ParameterType.IsByRef) {
 416                    hasByRefOrOut = true;
 417                    Type refType = typeof(StrongBox<>).MakeGenericType(pi.ParameterType.GetElementType());
 418                    var param = new ParameterWrapper(_binder, pi, refType, pi.Name, true, false, false);
 419                    parameters.Add(param);
 420                    ab = new ReferenceArgBuilder(pi, refType, indexForArgBuilder);
 421                } else {
 422                    hasByRefOrOut |= CompilerHelpers.IsOutParameter(pi);
 423                    var param = new ParameterWrapper(_binder, pi);
 424                    parameters.Add(param);
 425                    ab = new SimpleArgBuilder(pi, indexForArgBuilder);
 426                }
 427
 428                if (kwIndex == ParameterNotPassedByKeyword) {
 429                    arguments.Add(ab);
 430                } else {
 431                    Debug.Assert(KeywordArgBuilder.BuilderExpectsSingleParameter(ab));
 432                    arguments.Add(new KeywordArgBuilder(ab, _kwArgs.Length, kwIndex));
 433                }
 434            }
 435
 436            ReturnBuilder returnBuilder = MakeKeywordReturnBuilder(
 437                new ReturnBuilder(CompilerHelpers.GetReturnType(method)),
 438                parameterInfos,
 439                parameters,
 440                _binder.AllowKeywordArgumentSetting(method));
 441
 442            if (hasDefaults) {
 443                for (int defaultsUsed = 1; defaultsUsed < defaultArguments.Count + 1; defaultsUsed++) {
 444                    // if the left most default we'll use is not present then don't add a default.  This happens in cases such as:
 445                    // a(a=1, b=2, c=3) and then call with a(a=5, c=3).  We'll come through once for c (no default, skip),
 446                    // once for b (default present, emit) and then a (no default, skip again).  W/o skipping we'd generate the same
 447                    // method multiple times.  This also happens w/ non-contigious default values, e.g. foo(a, b=3, c) where we don't want
 448                    // to generate a default candidate for just c which matches the normal method.
 449                    if (defaultArguments[defaultArguments.Count - defaultsUsed] != null) {
 450                        AddSimpleTarget(MakeDefaultCandidate(
 451                            method,
 452                            parameters,
 453                            instanceBuilder,
 454                            arguments,
 455                            defaultArguments,
 456                            returnBuilder,
 457                            defaultsUsed));
 458                    }
 459                }
 460            }
 461
 462            if (hasByRefOrOut) {
 463                AddSimpleTarget(MakeByRefReducedMethodTarget(binder, parameterInfos, method));
 464            }
 465
 466            AddSimpleTarget(MakeMethodCandidate(method, parameters, instanceBuilder, arguments, returnBuilder));
 467        }
 468
 469        private MethodCandidate MakeDefaultCandidate(MethodBase method, List<ParameterWrapper> parameters, ArgBuilder instanceBuilder, List<ArgBuilder> argBuilders, List<ArgBuilder> defaultBuilders, ReturnBuilder returnBuilder, int defaultsUsed) {
 470            List<ArgBuilder> defaultArgBuilders = new List<ArgBuilder>(argBuilders);
 471            List<ParameterWrapper> necessaryParams = parameters.GetRange(0, parameters.Count - defaultsUsed);
 472
 473            for (int curDefault = 0; curDefault < defaultsUsed; curDefault++) {
 474                int readIndex = defaultBuilders.Count - defaultsUsed + curDefault;
 475                int writeIndex = defaultArgBuilders.Count - defaultsUsed + curDefault;
 476
 477                if (defaultBuilders[readIndex] != null) {
 478                    defaultArgBuilders[writeIndex] = defaultBuilders[readIndex];
 479                } else {
 480                    necessaryParams.Add(parameters[parameters.Count - defaultsUsed + curDefault]);
 481                }
 482            }
 483
 484            // shift any arguments forward that need to be...
 485            int curArg = CompilerHelpers.IsStatic(method) ? 0 : 1;
 486            for (int i = 0; i < defaultArgBuilders.Count; i++) {
 487                SimpleArgBuilder sab = defaultArgBuilders[i] as SimpleArgBuilder;
 488                if (sab != null) {
 489                    defaultArgBuilders[i] = sab.MakeCopy(curArg++);
 490                }
 491            }
 492
 493            return MakeMethodCandidate(method, necessaryParams, instanceBuilder, defaultArgBuilders, returnBuilder);
 494        }
 495
 496        private MethodCandidate MakeByRefReducedMethodTarget(ActionBinder binder, ParameterInfo[] parameterInfos, MethodBase method) {
 497            Assert.NotNull(binder, parameterInfos, method);
 498
 499            var parameters = new List<ParameterWrapper>();
 500            var arguments = new List<ArgBuilder>();
 501            int argIndex = 0;
 502            var instanceBuilder = MakeInstanceBuilder(binder, method, parameters, ref argIndex);            
 503            
 504            List<int> returnArgs = new List<int>();
 505            if (CompilerHelpers.GetReturnType(method) != typeof(void)) {
 506                returnArgs.Add(-1);
 507            }
 508
 509            var infoIndex = binder.PrepareParametersBinding(parameterInfos, arguments, parameters, ref argIndex);
 510            for (; infoIndex < parameterInfos.Length; infoIndex++) {
 511                var pi = parameterInfos[infoIndex];
 512
 513                if (binder.BindSpecialParameter(pi, arguments, parameters, ref argIndex)) {
 514                    continue;
 515                }
 516                
 517                // See KeywordArgBuilder.BuilderExpectsSingleParameter
 518                int indexForArgBuilder = 0;
 519
 520                int kwIndex = ParameterNotPassedByKeyword;
 521                if (!CompilerHelpers.IsOutParameter(pi)) {
 522                    kwIndex = GetKeywordIndex(pi);
 523                    if (kwIndex == ParameterNotPassedByKeyword) {
 524                        indexForArgBuilder = argIndex++;
 525                    }
 526                }
 527
 528                ArgBuilder ab;
 529                if (CompilerHelpers.IsOutParameter(pi)) {
 530                    returnArgs.Add(arguments.Count);
 531                    ab = new OutArgBuilder(pi);
 532                } else if (pi.ParameterType.IsByRef) {
 533                    // if the parameter is marked as [In] it is not returned.
 534                    if ((pi.Attributes & (ParameterAttributes.In | ParameterAttributes.Out)) != ParameterAttributes.In) {
 535                        returnArgs.Add(arguments.Count);
 536                    }
 537                    ParameterWrapper param = new ParameterWrapper(_binder, pi, pi.ParameterType.GetElementType(), pi.Name, false, false, false);
 538                    parameters.Add(param);
 539                    ab = new ReturnReferenceArgBuilder(pi, indexForArgBuilder);
 540                } else {
 541                    ParameterWrapper param = new ParameterWrapper(_binder, pi);
 542                    parameters.Add(param);
 543                    ab = new SimpleArgBuilder(pi, indexForArgBuilder);
 544                }
 545
 546                if (kwIndex == ParameterNotPassedByKeyword) {
 547                    arguments.Add(ab);
 548                } else {
 549                    Debug.Assert(KeywordArgBuilder.BuilderExpectsSingleParameter(ab));
 550                    arguments.Add(new KeywordArgBuilder(ab, _kwArgs.Length, kwIndex));
 551                }
 552            }
 553
 554            ReturnBuilder returnBuilder = MakeKeywordReturnBuilder(
 555                new ByRefReturnBuilder(returnArgs),
 556                parameterInfos,
 557                parameters,
 558                _binder.AllowKeywordArgumentSetting(method));
 559
 560            return MakeMethodCandidate(method, parameters, instanceBuilder, arguments, returnBuilder);
 561        }
 562
 563        private MethodCandidate MakeMethodCandidate(MethodBase method, List<ParameterWrapper> parameters, ArgBuilder instanceBuilder, List<ArgBuilder> argBuilders, ReturnBuilder returnBuilder) {
 564            return new MethodCandidate(
 565                new MethodTarget(this, method, parameters.Count, instanceBuilder, argBuilders, returnBuilder),
 566                parameters);
 567        }
 568
 569        private void GetMinAndMaxArgs(out int minArgs, out int maxArgs) {
 570            List<int> argCounts = new List<int>(_targetSets.Keys);
 571            argCounts.Sort();
 572            minArgs = argCounts[0];
 573            maxArgs = argCounts[argCounts.Count - 1];
 574        }
 575
 576        private static bool IsUnsupported(MethodBase method) {
 577            return (method.CallingConvention & CallingConventions.VarArgs) != 0 || method.ContainsGenericParameters;
 578        }
 579
 580        #endregion
 581
 582        #region Keyword arg binding support
 583
 584        private ReturnBuilder MakeKeywordReturnBuilder(ReturnBuilder returnBuilder, ParameterInfo[] methodParams, List<ParameterWrapper> parameters, bool isConstructor) {
 585            if (isConstructor) {
 586                List<string> unusedNames = GetUnusedKeywordParameters(methodParams);
 587                List<MemberInfo> bindableMembers = GetBindableMembers(returnBuilder, unusedNames);
 588                List<int> kwArgIndexs = new List<int>();
 589                if (unusedNames.Count == bindableMembers.Count) {
 590
 591                    foreach (MemberInfo mi in bindableMembers) {
 592                        ParameterWrapper pw = new ParameterWrapper(
 593                            _binder,
 594                            mi.MemberType == MemberTypes.Property ? 
 595                                ((PropertyInfo)mi).PropertyType :
 596                                ((FieldInfo)mi).FieldType,
 597                            mi.Name,
 598                            false);
 599
 600                        parameters.Add(pw);
 601                        kwArgIndexs.Add(GetKeywordIndex(mi.Name));
 602                    }
 603
 604                    KeywordConstructorReturnBuilder kwBuilder = new KeywordConstructorReturnBuilder(returnBuilder,
 605                        _kwArgs.Length,
 606                        kwArgIndexs.ToArray(),
 607                        bindableMembers.ToArray(),
 608                        _binder.PrivateBinding);
 609
 610                    return kwBuilder;
 611                }
 612
 613            }
 614            return returnBuilder;
 615        }
 616
 617        private static List<MemberInfo> GetBindableMembers(ReturnBuilder returnBuilder, List<string> unusedNames) {
 618            List<MemberInfo> bindableMembers = new List<MemberInfo>();
 619
 620            foreach (string name in unusedNames) {
 621                Type curType = returnBuilder.ReturnType;
 622                MemberInfo[] mis = curType.GetMember(name);
 623                while (mis.Length != 1 && curType != null) {
 624                    // see if we have a single member defined as the closest level
 625                    mis = curType.GetMember(name, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.SetField | BindingFlags.SetProperty | BindingFlags.Instance);
 626
 627                    if (mis.Length > 1) {
 628                        break;
 629                    }
 630
 631                    curType = curType.BaseType;
 632                }
 633
 634                if (mis.Length == 1) {
 635                    switch (mis[0].MemberType) {
 636                        case MemberTypes.Property:
 637                        case MemberTypes.Field:
 638                            bindableMembers.Add(mis[0]);
 639                            break;
 640                    }
 641                }
 642            }
 643            return bindableMembers;
 644        }
 645
 646        private List<string> GetUnusedKeywordParameters(ParameterInfo[] methodParams) {
 647            List<string> unusedNames = new List<string>();
 648            foreach (string name in _kwArgs) {
 649                bool found = false;
 650                foreach (ParameterInfo pi in methodParams) {
 651                    if (pi.Name == name) {
 652                        found = true;
 653                        break;
 654                    }
 655                }
 656                if (!found) {
 657                    unusedNames.Add(name);
 658                }
 659            }
 660            return unusedNames;
 661        }
 662
 663        private const int ParameterNotPassedByKeyword = -1;
 664
 665        // Check if the given parameter from the candidate's signature matches a keyword argument from the callsite.
 666        // Return ParameterNotPassedByKeyword if no match. Else returns index into callsite's keyword arglist if there is a match.
 667        private int GetKeywordIndex(ParameterInfo pi) {
 668            return GetKeywordIndex(pi.Name);
 669        }
 670
 671        private int GetKeywordIndex(string kwName) {
 672            for (int i = 0; i < _kwArgs.Length; i++) {
 673                if (kwName == _kwArgs[i]) {
 674                    return i;
 675                }
 676            }
 677            return ParameterNotPassedByKeyword;
 678        }
 679
 680        #endregion
 681
 682        #region TargetSet
 683
 684        /// <summary>
 685        /// Represents a collection of MethodCandidate's which all accept the
 686        /// same number of logical parameters.  For example a params method
 687        /// and a method with 3 parameters would both be a TargetSet for 3 parameters.
 688        /// </summary>
 689        internal class TargetSet {
 690            private MethodBinder _binder;
 691            private int _count;
 692            internal List<MethodCandidate> _targets;
 693
 694            internal TargetSet(MethodBinder binder, int count) {
 695                _count = count;
 696                _targets = new List<MethodCandidate>();
 697                _binder = binder;
 698            }
 699
 700            internal bool IsParamsDictionaryOnly() {
 701                foreach (MethodCandidate target in _targets) {
 702                    if (!target.HasParamsDictionary()) {
 703                        return false;
 704                    }
 705                }
 706                return true;
 707            }
 708
 709            // OBSOLETE
 710            internal BindingTarget MakeBindingTarget(CallTypes callType, Type[] types, string[] names, NarrowingLevel minLevel, NarrowingLevel maxLevel) {
 711                List<ConversionResult> lastFail = new List<ConversionResult>();
 712                List<CallFailure> failures = null;
 713
 714                // go through all available narrowing levels selecting candidates.  
 715                for (NarrowingLevel level = minLevel; level <= maxLevel; level++) {
 716                    List<MethodCandidate> applicableTargets = new List<MethodCandidate>();
 717                    if (failures != null) {
 718                        failures.Clear();
 719                    }
 720
 721                    foreach (MethodCandidate target in _targets) {
 722                        // skip params dictionaries - we want to only pick up the methods normalized
 723                        // to have argument names (which we created because the MethodBinder gets 
 724                        // created w/ keyword arguments).
 725                        if (!target.HasParamsDictionary()) {
 726                            Type[] normalizedTypes;
 727                            CallFailure callFailure;
 728
 729                            if (!target.TryGetNormalizedArguments(types, names, out normalizedTypes, out callFailure)) {
 730                                // dup keyword arguments or unassigned keyword argument
 731                                if (failures == null) failures = new List<CallFailure>(1);
 732                                failures.Add(callFailure);
 733                            } else if (target.IsApplicable(normalizedTypes, level, lastFail)) {
 734                                // success, remember the candidate...
 735                                applicableTargets.Add(GetCandidate(target, level));
 736                            } else {
 737                                // conversion failure, remember the failures...
 738                                if (failures == null) failures = new List<CallFailure>(1);
 739
 740                                failures.Add(new CallFailure(target.Target, lastFail.ToArray()));
 741                                lastFail.Clear();
 742                            }
 743                        }
 744                    }
 745
 746                    // see if we managed to get a single method or if one method is better...
 747                    List<MethodCandidate> result;
 748                    if (TryGetApplicableTarget(callType, applicableTargets, types, out result)) {
 749                        if (result.Count == 1) {
 750                            // only a single method is callable, success!
 751                            return MakeSuccessfulBindingTarget(callType, types, result);
 752                        }
 753
 754                        // more than one method found, no clear best method, report an ambigious match
 755                        return MakeAmbiguousBindingTarget(callType, types, result);
 756                    }
 757                }
 758
 759                Debug.Assert(failures != null);
 760                return new BindingTarget(_binder.Name, callType == CallTypes.None ? types.Length : types.Length - 1, failures.ToArray());
 761            }
 762
 763            internal BindingTarget MakeBindingTarget(CallTypes callType, DynamicMetaObject[] metaObjects, string[] names, NarrowingLevel minLevel, NarrowingLevel maxLevel) {
 764                List<ConversionResult> lastFail = new List<ConversionResult>();
 765                List<CallFailure> failures = null;
 766
 767                // go through all available narrowing levels selecting candidates.  
 768                for (NarrowingLevel level = minLevel; level <= maxLevel; level++) {
 769                    List<MethodCandidate> applicableTargets = new List<MethodCandidate>();
 770                    if (failures != null) {
 771                        failures.Clear();
 772                    }
 773
 774                    foreach (MethodCandidate target in _targets) {
 775                        // skip params dictionaries - we want to only pick up the methods normalized
 776                        // to have argument names (which we created because the MethodBinder gets 
 777                        // created w/ keyword arguments).
 778                        if (!target.HasParamsDictionary()) {
 779                            DynamicMetaObject[] normalizedObjects;
 780                            CallFailure callFailure;
 781
 782                            if (!target.TryGetNormalizedArguments(metaObjects, names, out normalizedObjects, out callFailure)) {
 783                                // dup keyword arguments or unassigned keyword argument
 784                                if (failures == null) failures = new List<CallFailure>(1);
 785                                failures.Add(callFailure);
 786                            } else if (target.IsApplicable(normalizedObjects, level, lastFail)) {
 787                                // success, remember the candidate...
 788                                applicableTargets.Add(GetCandidate(target, level));
 789                            } else {
 790                                // conversion failure, remember the failures...
 791                                if (failures == null) failures = new List<CallFailure>(1);
 792
 793                                failures.Add(new CallFailure(target.Target, lastFail.ToArray()));
 794                                lastFail.Clear();
 795                            }
 796                        }
 797                    }
 798
 799                    // see if we managed to get a single method or if one method is better...
 800                    List<MethodCandidate> result;
 801                    if (TryGetApplicableTarget(callType, applicableTargets, metaObjects, out result)) {
 802                        if (result.Count == 1) {
 803                            // only a single method is callable, success!
 804                            return MakeSuccessfulBindingTarget(callType, metaObjects, result);
 805                        }
 806
 807                        // more than one method found, no clear best method, report an ambigious match
 808                        return MakeAmbiguousBindingTarget(callType, metaObjects, result);
 809                    }
 810                }
 811
 812                Debug.Assert(failures != null);
 813                return new BindingTarget(_binder.Name, callType == CallTypes.None ? metaObjects.Length : metaObjects.Length - 1, failures.ToArray());
 814            }
 815
 816            private static bool TryGetApplicableTarget(CallTypes callType, List<MethodCandidate> applicableTargets, Type[] actualTypes, out List<MethodCandidate> result) {
 817                result = null;
 818                if (applicableTargets.Count == 1) {
 819                    result = applicableTargets;
 820                    return true;
 821                }
 822                if (applicableTargets.Count > 1) {
 823                    MethodCandidate target = FindBest(callType, applicableTargets, actualTypes);
 824                    if (target != null) {
 825                        result = new List<MethodCandidate>(new MethodCandidate[] { target });
 826                        return true;
 827                    } else {
 828                        result = applicableTargets;
 829                        return true;
 830                    }
 831                }
 832                return false;
 833            }
 834
 835            private static bool TryGetApplicableTarget(CallTypes callType, List<MethodCandidate> applicableTargets, DynamicMetaObject[] actualTypes, out List<MethodCandidate> result) {
 836                result = null;
 837                if (applicableTargets.Count == 1) {
 838                    result = applicableTargets;
 839                    return true;
 840                }
 841                if (applicableTargets.Count > 1) {
 842                    MethodCandidate target = FindBest(callType, applicableTargets, actualTypes);
 843                    if (target != null) {
 844                        result = new List<MethodCandidate>(new MethodCandidate[] { target });
 845                        return true;
 846                    } else {
 847                        result = applicableTargets;
 848                        return true;
 849                    }
 850                }
 851                return false;
 852            }
 853
 854            // OBSOLETE
 855            private Type[] GetTypesForTest(Type[] types, IList<MethodCandidate> candidates) {
 856                // if we have a single target we need no tests.
 857                if (_targets.Count == 1) return null;
 858
 859                Type[] tests = new Type[types.Length];
 860                for (int i = 0; i < types.Length; i++) {
 861                    if (AreArgumentTypesOverloaded(i, types.Length, candidates)) {
 862                        tests[i] = types[i];
 863                    }
 864                }
 865
 866                return tests;
 867            }
 868
 869            private RestrictionInfo GetRestrictedMetaObjects(MethodCandidate target, DynamicMetaObject[] objects, IList<MethodCandidate> candidates) {
 870                IList<ParameterWrapper> parameters = target.Parameters;
 871
 872                Debug.Assert(parameters.Count == objects.Length);
 873
 874                DynamicMetaObject[] resObjects = new DynamicMetaObject[objects.Length];
 875                Type[] types = new Type[objects.Length];
 876
 877                for (int i = 0; i < objects.Length; i++) {
 878                    if (_targets.Count > 0 && AreArgumentTypesOverloaded(i, objects.Length, candidates)) {                                                
 879                        resObjects[i] = RestrictOne(objects[i], parameters[i]);
 880                        types[i] = objects[i].GetLimitType();
 881                    } else if (parameters[i].Type.IsAssignableFrom(objects[i].Expression.Type)) {
 882                        // we have a strong enough type already
 883                        resObjects[i] = objects[i];
 884                    } else {
 885                        resObjects[i] = RestrictOne(objects[i], parameters[i]);
 886                        types[i] = objects[i].GetLimitType();
 887                    }
 888                }
 889
 890                return new RestrictionInfo(resObjects, types);
 891            }
 892
 893            private DynamicMetaObject RestrictOne(DynamicMetaObject obj, ParameterWrapper forParam) {
 894                if (forParam.Type == typeof(object)) {
 895                    // don't use Restrict as it'll box & unbox.
 896                    return new DynamicMetaObject(obj.Expression, BindingRestrictionsHelpers.GetRuntimeTypeRestriction(obj.Expression, obj.GetLimitType()));
 897                } else {
 898                    return obj.Restrict(obj.GetLimitType());
 899                }
 900            }
 901
 902            private static bool AreArgumentTypesOverloaded(int argIndex, int argCount, IList<MethodCandidate> methods) {
 903                Type argType = null;
 904                for (int i = 0; i < methods.Count; i++) {
 905                    IList<ParameterWrapper> pis = methods[i].Parameters;
 906                    if (pis.Count == 0) continue;
 907
 908                    int readIndex = argIndex;
 909                    if (pis[0].Type == typeof(CodeContext)) {
 910                        readIndex++;
 911                    }
 912
 913                    Type curType;
 914                    if (readIndex < pis.Count) {
 915                        if (readIndex == -1) {
 916                            curType = methods[i].Target.Method.DeclaringType;
 917                        } else if (pis[readIndex].IsParamsArray) {
 918                            if (argIndex == argCount - (pis.Count - readIndex)) {
 919                                // We're the params array argument and a single value is being passed
 920                                // directly to it.  The params array could be in the middle for
 921                                // a params setter.  so pis.Count - readIndex is usually 1 for the
 922                                // params at the end, and therefore types.Length - 1 is usually if we're
 923                                // the last argument.  We always have to check this type to disambiguate
 924                                // between passing an object which is compatible with the arg array and
 925                                // passing an object which goes into the arg array.  Maybe we could do 
 926                                // better sometimes.
 927                                return true;
 928                            }
 929                            curType = pis[pis.Count - 1].Type.GetElementType();
 930                        } else {
 931                            curType = pis[readIndex].Type;
 932                        }
 933                    } else if (pis[pis.Count - 1].IsParamsArray) {
 934                        curType = pis[pis.Count - 1].Type.GetElementType();
 935                    } else {
 936                        continue;
 937                    }
 938
 939                    if (argType == null) {
 940                        argType = curType;
 941                    } else if (argType != curType) {
 942                        return true;
 943                    }
 944                }
 945                return false;
 946            }
 947
 948            private static bool IsBest(MethodCandidate candidate, List<MethodCandidate> applicableTargets, CallTypes callType, Type[] actualTypes) {
 949                foreach (MethodCandidate target in applicableTargets) {
 950                    if (candidate == target) {
 951                        continue;
 952                    }
 953
 954                    if (MethodCandidate.GetPreferredCandidate(candidate, target, callType, actualTypes) != Candidate.One) {
 955                        return false;
 956                    }
 957                }
 958                return true;
 959            }
 960
 961            private static MethodCandidate FindBest(CallTypes callType, List<MethodCandidate> applicableTargets, Type[] actualTypes) {
 962                foreach (MethodCandidate candidate in applicableTargets) {
 963                    if (IsBest(candidate, applicableTargets, callType, actualTypes)) return candidate;
 964                }
 965                return null;
 966            }
 967
 968            private static bool IsBest(MethodCandidate candidate, List<MethodCandidate> applicableTargets, CallTypes callType, DynamicMetaObject[] actualTypes) {
 969                foreach (MethodCandidate target in applicableTargets) {
 970                    if (candidate == target) {
 971                        continue;
 972                    }
 973
 974                    if (MethodCandidate.GetPreferredCandidate(candidate, target, callType, actualTypes) != Candidate.One) {
 975                        return false;
 976                    }
 977                }
 978                return true;
 979            }
 980
 981            private static MethodCandidate FindBest(CallTypes callType, List<MethodCandidate> applicableTargets, DynamicMetaObject[] actualTypes) {
 982                foreach (MethodCandidate candidate in applicableTargets) {
 983                    if (IsBest(candidate, applicableTargets, callType, actualTypes)) return candidate;
 984                }
 985                return null;
 986            }
 987
 988            internal void Add(MethodCandidate target) {
 989                Debug.Assert(target.Parameters.Count == _count);
 990
 991                _targets.Add(target);
 992            }
 993
 994            private static MethodCandidate GetCandidate(MethodCandidate target, NarrowingLevel level) {
 995                if (level == NarrowingLevel.None) return target;
 996
 997                return new MethodCandidate(target, level);
 998            }
 999
1000            // OBSOLETE
1001            private BindingTarget MakeSuccessfulBindingTarget(CallTypes callType, Type[] types, List<MethodCandidate> result) {
1002                MethodCandidate resTarget = result[0];
1003                return new BindingTarget(_binder.Name, callType == CallTypes.None ? types.Length : types.Length - 1, resTarget.Target, resTarget.NarrowingLevel, GetTypesForTest(types, _targets));
1004            }
1005
1006            private BindingTarget MakeSuccessfulBindingTarget(CallTypes callType, DynamicMetaObject[] objects, List<MethodCandidate> result) {
1007                MethodCandidate resTarget = result[0];
1008                return new BindingTarget(_binder.Name, callType == CallTypes.None ? objects.Length : objects.Length - 1, resTarget.Target, resTarget.NarrowingLevel, GetRestrictedMetaObjec

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