예제 #1
0
        public MethodResolution(MethodInfo methodInfo, Closure closure, InvokeExpression callAst)
        {
            MethodInfo = methodInfo;
            Closure = closure;
            CallAst = callAst;
            Result = null;

            PassesComplete = null;
            Target = new StronglyTypedAstBuilder(closure).Visit(callAst.Target);
            FormalArgs = new List<Type>(MethodInfo.GetParameters().Select(p => p.ParameterType));
            RealArgs = new List<Expression>(callAst.Args.Select(ast => ast is LambdaExpression ?
                new StronglyTypedAstBuilder(closure).VisitLambda((LambdaExpression)ast, null, true) :
                new StronglyTypedAstBuilder(closure).Visit(ast)));
            InferenceCache = new Dictionary<Type, Type>();
            Array.ForEach(MethodInfo.GetGenericArguments(), t => InferenceCache.Add(t, null));
        }
예제 #2
0
        public bool? Resolve(ResolutionPass pass)
        {
            // Guard ourselves from invalid/unnecessary calls
            if (Result == false) return false;
            if (PassesComplete == null && pass != ResolutionPass.First) throw new ArgumentException(String.Format(
                "Cannot execute resolution pass '{0}'. Expecting '{1}'", pass, ResolutionPass.First));
            if (PassesComplete == ResolutionPass.First && pass != ResolutionPass.Second) throw new ArgumentException(String.Format(
                "Cannot execute resolution pass '{0}'. Expecting '{1}'", pass, ResolutionPass.Second));
            if (PassesComplete == ResolutionPass.Second) return Result;

            if (pass == ResolutionPass.Second)
            {
                if (MethodInfo.IsGenericMethodDefinition)
                {
                    // Lez infer lambda arguments using knowledge about method generic argument types
                    // upd. Along with that we'll infer stuff that wasn't touched during the first pass

                    for (var i = 0; i < FormalArgs.Count; ++i)
                    {
                        if (FormalArgs[i].IsFunctionType())
                        {
                            var fFormal = FormalArgs[i].GetFunctionDesc();
                            var resolvedLambdaArgs = fFormal.Args.Select(lambdaArg => ResolveGenericArgs(lambdaArg));

                            if (resolvedLambdaArgs.Any(resolvedLambdaArg => resolvedLambdaArg == null))
                            {
                                throw new ArgumentException("First pass of resolution failed: " +
                                    "not all types necessary to fully resolve lambda parameters were inferred.");
                            }
                            else
                            {
                                RealArgs[i] = new StronglyTypedAstBuilder(Closure).VisitLambda(
                                    (LambdaExpression)CallAst.Args.ElementAt(i),
                                    resolvedLambdaArgs.ToArray());

                                var realArgType = RealArgs[i].GetType().IsLambda() && FormalArgs[i].IsLambda() ?
                                   RealArgs[i].GetType() : RealArgs[i].Type;
                                MatchTypesExact(FormalArgs[i], realArgType, ResolutionPass.Second, true);
                            }
                        }
                    }

                    // Everything should be inferred at this moment, or ALL ABOARD THE FAILBOAT
                    if (InferenceCache.Any(kvp => kvp.Value == null))
                    {
                        throw new ArgumentException("First pass of resolution failed: not all generic arguments had been inferred");
                    }

                    MethodInfo = MethodInfo.MakeGenericMethod(InferenceCache.Values.ToArray());
                    FormalArgs = new List<Type>(MethodInfo.GetParameters().Select(p => p.ParameterType));
                    InferenceCache = new Dictionary<Type, Type>();
                }
            }

            // Screw varargs methods for now
            var formalArgs = MethodInfo.GetParameters().Select(p => p.ParameterType).ToArray();
            var realArgs = RealArgs.Select((arg, i) => arg.GetType().IsLambda() && FormalArgs[i].IsLambda() ? arg.GetType() : arg.Type).ToArray();

            if (formalArgs.Length == realArgs.Length)
            {
                Result = true;

                for (var i = 0; i < formalArgs.Length; i++)
                {
                    var formal = formalArgs[i];
                    var real = realArgs[i];

                    var matchResult = MatchTypesAllowInheritance(formal, real, pass);
                    Result = Result.Accumulate(matchResult);
                }

                // first version of method inference assumed that at this point all generic args are inferred
                // however this approach is invalid since return result might need some help from lambda parameters
                // example: IQueryable<TResult> Select<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource,TResult>> selector)
                // damn that was so elegant...
            }
            else
            {
                Result = false;
            }

            // Update information about state
            if (PassesComplete == ResolutionPass.First) PassesComplete = ResolutionPass.Second;
            if (PassesComplete == null) PassesComplete = ResolutionPass.First;

            return Result;
        }