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)); }
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; }