Example #1
0
        ///
        /// Determines if the candidate method is applicable (section 14.4.2.1)
        /// to the given set of arguments
        /// A return value rates candidate method compatibility,
        /// 0 = the best, int.MaxValue = the worst
        ///
        public int IsApplicable(ResolveContext ec,
            ref Arguments arguments, int arg_count, ref MethodSpec method, ref bool params_expanded_form)
        {
            var candidate = method;

            AParametersCollection pd = candidate.Parameters;
            int param_count = GetApplicableParametersCount (candidate, pd);
            int optional_count = 0;

            if (arg_count != param_count) {
                for (int i = 0; i < pd.Count; ++i) {
                    if (pd.FixedParameters [i].HasDefaultValue) {
                        optional_count = pd.Count - i;
                        break;
                    }
                }

                int args_gap = System.Math.Abs (arg_count - param_count);
                if (optional_count != 0) {
                    if (args_gap > optional_count)
                        return int.MaxValue - 10000 + args_gap - optional_count;

                    // Readjust expected number when params used
                    if (pd.HasParams) {
                        optional_count--;
                        if (arg_count < param_count)
                            param_count--;
                    } else if (arg_count > param_count) {
                        return int.MaxValue - 10000 + args_gap;
                    }
                } else if (arg_count != param_count) {
                    if (!pd.HasParams)
                        return int.MaxValue - 10000 + args_gap;
                    if (arg_count < param_count - 1)
                        return int.MaxValue - 10000 + args_gap;
                }

                // Initialize expanded form of a method with 1 params parameter
                params_expanded_form = param_count == 1 && pd.HasParams;

                // Resize to fit optional arguments
                if (optional_count != 0) {
                    Arguments resized;
                    if (arguments == null) {
                        resized = new Arguments (optional_count);
                    } else {
                        resized = new Arguments (param_count);
                        resized.AddRange (arguments);
                    }

                    for (int i = arg_count; i < param_count; ++i)
                        resized.Add (null);
                    arguments = resized;
                }
            }

            if (arg_count > 0) {
                //
                // Shuffle named arguments to the right positions if there are any
                //
                if (arguments [arg_count - 1] is NamedArgument) {
                    arg_count = arguments.Count;

                    for (int i = 0; i < arg_count; ++i) {
                        bool arg_moved = false;
                        while (true) {
                            NamedArgument na = arguments[i] as NamedArgument;
                            if (na == null)
                                break;

                            int index = pd.GetParameterIndexByName (na.Name);

                            // Named parameter not found or already reordered
                            if (index <= i)
                                break;

                            // When using parameters which should not be available to the user
                            if (index >= param_count)
                                break;

                            if (!arg_moved) {
                                arguments.MarkReorderedArgument (na);
                                arg_moved = true;
                            }

                            Argument temp = arguments[index];
                            arguments[index] = arguments[i];
                            arguments[i] = temp;

                            if (temp == null)
                                break;
                        }
                    }
                } else {
                    arg_count = arguments.Count;
                }
            } else if (arguments != null) {
                arg_count = arguments.Count;
            }

            //
            // 1. Handle generic method using type arguments when specified or type inference
            //
            if (candidate.IsGeneric) {
                if (type_arguments != null) {
                    var g_args_count = candidate.Arity;
                    if (g_args_count != type_arguments.Count)
                        return int.MaxValue - 20000 + System.Math.Abs (type_arguments.Count - g_args_count);

                    method = candidate.MakeGenericMethod (type_arguments.Arguments);
                    candidate = method;
                    pd = candidate.Parameters;
                } else {
                    int score = TypeManager.InferTypeArguments (ec, arguments, ref candidate);
                    if (score != 0)
                        return score - 20000;

                    pd = candidate.Parameters;
                }
            } else {
                if (type_arguments != null)
                    return int.MaxValue - 15000;
            }

            //
            // 2. Each argument has to be implicitly convertible to method parameter
            //
            method = candidate;
            Parameter.Modifier p_mod = 0;
            TypeSpec pt = null;
            for (int i = 0; i < arg_count; i++) {
                Argument a = arguments [i];
                if (a == null) {
                    if (!pd.FixedParameters [i].HasDefaultValue)
                        throw new InternalErrorException ();

                    Expression e = pd.FixedParameters [i].DefaultValue as Constant;
                    if (e == null)
                        e = new DefaultValueExpression (new TypeExpression (pd.Types [i], loc), loc).Resolve (ec);

                    arguments [i] = new Argument (e, Argument.AType.Default);
                    continue;
                }

                if (p_mod != Parameter.Modifier.PARAMS) {
                    p_mod = pd.FixedParameters [i].ModFlags & ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
                    pt = pd.Types [i];
                } else {
                    params_expanded_form = true;
                }

                Parameter.Modifier a_mod = a.Modifier & ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
                int score = 1;
                if (!params_expanded_form)
                    score = IsArgumentCompatible (ec, a_mod, a, p_mod & ~Parameter.Modifier.PARAMS, pt);

                if (score != 0 && (p_mod & Parameter.Modifier.PARAMS) != 0 && delegate_type == null) {
                    // It can be applicable in expanded form
                    score = IsArgumentCompatible (ec, a_mod, a, 0, TypeManager.GetElementType (pt));
                    if (score == 0)
                        params_expanded_form = true;
                }

                if (score != 0) {
                    if (params_expanded_form)
                        ++score;
                    return (arg_count - i) * 2 + score;
                }
            }

            if (arg_count != param_count)
                params_expanded_form = true;

            return 0;
        }