private TypeInferer(CandidateParameterInfo[] parameters, MethodInfo method, NameResolver resolver) { if (!method.IsGenericMethodDefinition) { throw new ArgumentException("The specified method must be a generic method definition."); } _parameters = parameters; _method = method; // take a copy of this because it may be modified to change implicitly-typed lambdas into “translated” LINQ expressions with the right explicit types _arguments = parameters.Select(p => p.Argument).ToArray(); _genericParameters = method.GetGenericArguments(); _fixed = new bool[_genericParameters.Length]; _inferred = new Type[_genericParameters.Length]; _bounds = new List <boundInfo> [_genericParameters.Length]; _resolver = resolver; }
/// <summary>See <see cref="CsExpression.ToLinqExpression"/>.</summary> public override Expression ToLinqExpression(NameResolver resolver, bool isChecked) { return Expression.Constant(Literal); }
internal override ResolveContext ToResolveContext(NameResolver resolver, bool isChecked) { // If parameter types are not specified, cannot turn into expression yet if (isImplicit()) return new ResolveContextLambda(this); return new ResolveContextExpression(ToLinqExpression(resolver, isChecked), wasAnonymousFunction: true); }
/// <summary> /// See <see cref="CsExpression.ToLinqExpression"/>.</summary> /// <param name="resolver"> /// See <see cref="CsExpression.ToLinqExpression"/>.</param> /// <param name="parameterTypes"> /// The types of the parameters of the lambda expression.</param> /// <param name="isChecked"> /// See <see cref="CsExpression.ToLinqExpression"/>.</param> /// <returns> /// An expression deriving from <see cref="System.Linq.Expressions.Expression"/>.</returns> public Expression ToLinqExpression(NameResolver resolver, Type[] parameterTypes, bool isChecked) { if (parameterTypes.Length != Parameters.Count) throw new ArgumentException("Number of supplied parameter types does not match number of parameters on the lambda."); var prmExprs = new ParameterExpression[Parameters.Count]; for (int i = 0; i < Parameters.Count; i++) { prmExprs[i] = Expression.Parameter(parameterTypes[i], Parameters[i].Name); resolver.AddLocalName(Parameters[i].Name, prmExprs[i]); } var body = Body.ToLinqExpression(resolver, isChecked); var lambda = Expression.Lambda(body, prmExprs); for (int i = 0; i < Parameters.Count; i++) resolver.ForgetLocalName(Parameters[i].Name); return lambda; }
/// <summary>See <see cref="CsExpression.ToLinqExpression"/>.</summary> public override Expression ToLinqExpression(NameResolver resolver, bool isChecked) { if (isImplicit()) throw new InvalidOperationException("The implicitly-typed lambda expression “{0}” cannot be translated to a LINQ expression without knowing the types of its parameters. Use the ToLinqExpression(NameResolver,Type[]) overload to specify the parameter types.".Fmt(ToString())); var prmTypes = new Type[Parameters.Count]; for (int i = 0; i < Parameters.Count; i++) prmTypes[i] = resolver.ResolveType(Parameters[i].Type); return ToLinqExpression(resolver, prmTypes, isChecked); }
public static IEnumerable <CandidateInfo <T> > EvaluateArgumentList <T>(T member, ParameterInfo[] parameterInfo, IEnumerable <ArgumentInfo> args, NameResolver resolver) where T : MemberInfo { var i = 0; var seenNamedArgument = false; var inParamsArray = false; var evaluatedParameterTypes = new AutoList <Type>(); var evaluatedArgumentIndex = new AutoList <int>(); var evaluatedArguments = new AutoList <ResolveContext>(); var modes = new AutoList <ArgumentMode>(parameterInfo.Select(pi => pi.IsOut ? ArgumentMode.Out : pi.ParameterType.IsByRef ? ArgumentMode.Ref : ArgumentMode.In)); var paramsArray = new List <ResolveContext>(); var paramsArrayIndex = parameterInfo.IndexOf(p => p.IsDefined(typeof(ParamArrayAttribute), false)); foreach (var argument in args) { if (argument.Name != null) { // Named argument seenNamedArgument = true; var index = parameterInfo.IndexOf(pi => pi.Name == argument.Name); if (index == -1) { // There is no parameter by this name ⇒ resolution fails yield break; } if (evaluatedArguments[index] != null) { throw new InvalidOperationException("The named parameter “{0}” has been specified multiple times.".Fmt(argument.Name)); } if (argument.Mode != modes[index]) { // in/out/ref doesn’t match parameter ⇒ resolution fails yield break; } evaluatedArguments[index] = argument.Argument; evaluatedParameterTypes[index] = parameterInfo[index].ParameterType; evaluatedArgumentIndex[index] = i; } else { // Positional argument if (seenNamedArgument) { throw new InvalidOperationException("Cannot use a positional argument after a named argument."); } if (i >= parameterInfo.Length && !inParamsArray) { yield break; // too many positional arguments: candidate is inapplicable } if (paramsArrayIndex != -1 && i >= paramsArrayIndex) { if (argument.Mode != ArgumentMode.In) { // Can’t use out/ref in a params array yield break; } paramsArray.Add(argument.Argument); } else { if (argument.Mode != modes[i]) { // in/out/ref doesn’t match parameter ⇒ resolution fails yield break; } evaluatedArguments[i] = argument.Argument; evaluatedParameterTypes[i] = parameterInfo[i].ParameterType; evaluatedArgumentIndex[i] = i; } } i++; } // Fill in the default values for the unspecified optional parameters for (int j = 0; j < parameterInfo.Length; j++) { if (j != paramsArrayIndex && evaluatedArguments[j] == null) { if ((parameterInfo[j].Attributes & ParameterAttributes.HasDefault) != 0) { evaluatedArguments[j] = new ResolveContextConstant(parameterInfo[j].DefaultValue, parameterInfo[j].ParameterType); evaluatedParameterTypes[j] = parameterInfo[j].ParameterType; evaluatedArgumentIndex[j] = -1; } else { yield break; // a non-optional parameter has no argument specified ⇒ not a candidate } } } Func <int, CandidateParameterInfo[]> getParameters = count => Enumerable.Range(0, count).Select(p => new CandidateParameterInfo { ParameterType = evaluatedParameterTypes[p], Mode = modes[p], Argument = evaluatedArguments[p], ArgumentIndex = evaluatedArgumentIndex[p], UninstantiatedParameterType = evaluatedParameterTypes[p], }).ToArray(); if (paramsArrayIndex == -1 || paramsArray.Count == 1) { if (paramsArrayIndex != -1) { // Emit the normal form evaluatedArguments[paramsArrayIndex] = paramsArray[0]; evaluatedParameterTypes[paramsArrayIndex] = parameterInfo[paramsArrayIndex].ParameterType; } yield return(new CandidateInfo <T>(member, getParameters(evaluatedParameterTypes.Count), parameterInfo.Length, isLiftedOperator: false, isExpandedForm: false)); } if (paramsArrayIndex != -1) { // Emit the expanded form var arrayElementType = parameterInfo[paramsArrayIndex].ParameterType.GetElementType(); for (int k = 0; k < paramsArray.Count; k++) { evaluatedArguments[paramsArrayIndex + k] = paramsArray[k]; evaluatedParameterTypes[paramsArrayIndex + k] = arrayElementType; evaluatedArgumentIndex[paramsArrayIndex + k] = paramsArrayIndex + k; modes[paramsArrayIndex + k] = ArgumentMode.In; } // If paramsArray.Count == 0, then the expanded form is *shorter* than the normal form yield return(new CandidateInfo <T>(member, getParameters(paramsArrayIndex + paramsArray.Count), parameterInfo.Length, isLiftedOperator: false, isExpandedForm: true)); } }
/// <summary>See <see cref="CsExpression.ToLinqExpression"/>.</summary> public override Expression ToLinqExpression(NameResolver resolver, bool isChecked) { return ToResolveContext(resolver, isChecked).ToExpression(); }
/// <summary>See <see cref="CsExpression.ToLinqExpression"/>.</summary> public override Expression ToLinqExpression(NameResolver resolver, bool isChecked) { var operand = ToResolveContext(Operand, resolver, isChecked); var type = resolver.ResolveType(Type); if (operand is ResolveContextLambda) throw new NotImplementedException(); return Expression.Convert(operand.ToExpression(), type); }
internal override ResolveContext ToResolveContext(NameResolver resolver, bool isChecked) { if (Arguments.Any(a => a.Mode != ArgumentMode.In)) throw new NotImplementedException("out and ref parameters are not implemented."); var left = ToResolveContext(Left, resolver, isChecked); var resolvedArguments = Arguments.Select(a => new ArgumentInfo(a.Name, ToResolveContext(a.Expression, resolver, isChecked), a.Mode)); if (IsIndexer) { var property = ParserUtil.ResolveOverloads( left.ExpressionType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Select(p => Tuple.Create(p, p.GetIndexParameters())) .ToList(), resolvedArguments, resolver); throw new NotImplementedException(); } var leftMg = left as ResolveContextMethodGroup; if (leftMg != null) { // Try non-extension methods first, then extension methods for (int i = 0; i < 2; i++) { var method = ParserUtil.ResolveOverloads( leftMg.MethodGroup.Where(mg => mg.IsExtensionMethod == (i == 1)).Select(mg => Tuple.Create(mg.Method, mg.Method.GetParameters())).ToList(), // For extension methods, add the expression that pretends to be the “this” instance as the first argument i == 0 ? resolvedArguments : new[] { new ArgumentInfo(null, leftMg.Parent, ArgumentMode.In) }.Concat(resolvedArguments), resolver); if (method != null) return new ResolveContextExpression(Expression.Call(method.Member.IsStatic ? null : leftMg.Parent.ToExpression(), method.Member, method.Parameters.Select(arg => { var argExpr = arg.Argument.ToExpression(); return (argExpr.Type != arg.ParameterType) ? Expression.Convert(argExpr, arg.ParameterType) : argExpr; })), wasAnonymousFunction: false); } throw new InvalidOperationException("Cannot determine which method overload to use for “{0}”. Either type inference failed or the call is ambiguous.".Fmt(leftMg.MethodName)); } throw new NotImplementedException(); }
internal override ResolveContext ToResolveContext(NameResolver resolver, bool isChecked) { return resolver.ResolveSimpleName(Identifier); }
internal virtual ResolveContext ToResolveContext(NameResolver resolver, bool isChecked) { return new ResolveContextExpression(ToLinqExpression(resolver, isChecked)); }
internal static ResolveContext ToResolveContext(CsExpression expr, NameResolver resolver, bool isChecked) { return expr.ToResolveContext(resolver, isChecked); }
/// <summary> /// Converts this expression parse tree into an expression using the <c>System.Linq.Expressions</c> namespace. The /// expression thus returned can be used in scenarios surrounding <see cref="IQueryable"/>, such as LINQ-to-SQL, /// EntityFramework and many others.</summary> /// <param name="resolver"> /// Defines the rules of how to resolve names. Use this both to define which assemblies the code should be allowed /// to access as well as to declare variables and constants.</param> /// <param name="isChecked"> /// Specifies whether arithmetic operations (such as addition) should assume checked arithmetic or unchecked. The /// <c>checked()</c> and <c>unchecked()</c> expressions override this, so this only applies to expressions outside /// of those.</param> /// <returns> /// An expression deriving from <see cref="System.Linq.Expressions.Expression"/>.</returns> public abstract Expression ToLinqExpression(NameResolver resolver, bool isChecked);
/// <summary>See <see cref="CsExpression.ToLinqExpression"/>.</summary> public override Expression ToLinqExpression(NameResolver resolver, bool isChecked) { // Special case: if this is a unary minus operator that operates on a CsNumberLiteralExpression, // simply add the "-" and parse the number directly if (Operator == UnaryOperator.Minus && Operand is CsNumberLiteralExpression) return Expression.Constant(ParserUtil.ParseNumericLiteral("-" + ((CsNumberLiteralExpression) Operand).Literal)); throw new NotImplementedException(); }
internal override ResolveContext ToResolveContext(NameResolver resolver, bool isChecked) { if (AccessType == MemberAccessType.PointerDeref) throw new InvalidOperationException("Pointer dereference is not supported in LINQ expressions."); return resolver.ResolveSimpleName(Right, ToResolveContext(Left, resolver, isChecked)); }
/// <summary>See <see cref="CsExpression.ToLinqExpression"/>.</summary> public override Expression ToLinqExpression(NameResolver resolver, bool isChecked) { var left = Left.ToLinqExpression(resolver, isChecked); var right = Right.ToLinqExpression(resolver, isChecked); if (left.Type != right.Type) { var conv = Conversion.Implicit(left.Type, right.Type); if (conv != null) left = Expression.Convert(left, right.Type); else if ((conv = Conversion.Implicit(right.Type, left.Type)) != null) right = Expression.Convert(right, left.Type); } switch (Operator) { case BinaryOperator.Times: return isChecked ? Expression.MultiplyChecked(left, right) : Expression.Multiply(left, right); case BinaryOperator.Div: return Expression.Divide(left, right); case BinaryOperator.Mod: return Expression.Modulo(left, right); case BinaryOperator.Plus: return isChecked ? Expression.AddChecked(left, right) : Expression.Add(left, right); case BinaryOperator.Minus: return isChecked ? Expression.SubtractChecked(left, right) : Expression.Subtract(left, right); case BinaryOperator.Shl: return Expression.LeftShift(left, right); case BinaryOperator.Shr: return Expression.RightShift(left, right); case BinaryOperator.Less: return Expression.LessThan(left, right); case BinaryOperator.Greater: return Expression.GreaterThan(left, right); case BinaryOperator.LessEq: return Expression.LessThanOrEqual(left, right); case BinaryOperator.GreaterEq: return Expression.GreaterThanOrEqual(left, right); case BinaryOperator.Eq: return Expression.Equal(left, right); case BinaryOperator.NotEq: return Expression.NotEqual(left, right); case BinaryOperator.And: return Expression.And(left, right); case BinaryOperator.Xor: return Expression.ExclusiveOr(left, right); case BinaryOperator.Or: return Expression.Or(left, right); case BinaryOperator.AndAnd: return Expression.AndAlso(left, right); case BinaryOperator.OrOr: return Expression.OrElse(left, right); case BinaryOperator.Coalesce: return Expression.Coalesce(left, right); default: throw new InvalidOperationException("Unexpected binary operator: " + Operator); } }
/// <summary>See <see cref="CsExpression.ToLinqExpression"/>.</summary> public override Expression ToLinqExpression(NameResolver resolver, bool isChecked) { return Expression.Constant(ParserUtil.ParseNumericLiteral(Literal)); }
/// <summary>See <see cref="CsExpression.ToLinqExpression"/>.</summary> public override Expression ToLinqExpression(NameResolver resolver, bool isChecked) { throw new NotImplementedException(); }
/// <summary>See <see cref="CsExpression.ToLinqExpression"/>.</summary> public override Expression ToLinqExpression(NameResolver resolver, bool isChecked) { return Subexpression.ToLinqExpression(resolver, isChecked); }
public static CandidateInfo <T> ResolveOverloads <T>(List <Tuple <T, ParameterInfo[]> > overloads, IEnumerable <ArgumentInfo> arguments, NameResolver resolver) where T : MemberInfo { var candidates = overloads.SelectMany(ov => ParserUtil.EvaluateArgumentList(ov.Item1, ov.Item2, arguments, resolver)).ToList(); // Type inference for (int i = 0; i < candidates.Count; i++) { if (candidates[i].Member is MethodInfo && ((MethodInfo)(MemberInfo)candidates[i].Member).IsGenericMethodDefinition) { candidates[i] = TypeInferer.TypeInference(candidates[i], resolver); } } // Remove nulls (entries where type inference failed) and entries that are not applicable (§7.5.3.1 Applicable function member) candidates = candidates.Where(c => c != null && c.Parameters.All(p => p.Mode == ArgumentMode.In ? Conversion.Implicit(p.Argument, p.ParameterType) != null : p.ParameterType == p.Argument.ExpressionType)).ToList(); if (candidates.Count == 0) { return(null); } if (candidates.Count == 1) { return(candidates[0]); } // We have more than one candidate, so need to find the “best” one bool[] cannot = new bool[candidates.Count]; for (int i = 0; i < cannot.Length; i++) { for (int j = i + 1; j < cannot.Length; j++) { int compare = candidates[i].Better(candidates[j]); if (compare != 1) // j is not better { cannot[j] = true; } if (compare != -1) // i is not better { cannot[i] = true; } } } CandidateInfo <T> candidate = null; for (int i = 0; i < cannot.Length; i++) { if (!cannot[i]) { if (candidate == null) { candidate = candidates[i]; } else { // There is more than one applicable candidate — method call is ambiguous return(null); } } } // Either candidate == null, in which case no candidate was better than all others, or this is the successful candidate return(candidate); }
public static CandidateInfo <T> TypeInference <T>(CandidateInfo <T> candidateInfo, NameResolver resolver) where T : MemberInfo { var method = candidateInfo.Member as MethodInfo; if (method == null || !method.IsGenericMethodDefinition) { throw new InvalidOperationException("Type inference can only be performed on a generic method definition."); } var inferer = new TypeInferer(candidateInfo.Parameters, method, resolver); if (!inferer.infer()) { return(null); } var inferredMethod = method.MakeGenericMethod(inferer._inferred); // Return the list of parameter types with the generic type parameters substituted var parameterTypes = inferredMethod.GetParameters().Select(p => p.ParameterType).ToArray(); // The inferer may have changed an implicit lambda to a LINQ expression, so return the new version of _arguments return(new CandidateInfo <T>((T)(MemberInfo)inferredMethod, candidateInfo.Parameters.Select((p, i) => new CandidateParameterInfo { ParameterType = parameterTypes[i], Mode = p.Mode, Argument = inferer._arguments[i], ArgumentIndex = p.ArgumentIndex, UninstantiatedParameterType = p.ParameterType }).ToArray(), inferredMethod.GetParameters().Length, isLiftedOperator: false, isExpandedForm: candidateInfo.IsExpandedForm)); }