private void SetupUntypedLambdaParameters(params string[] parameterNames) { if (m_lambdaLeftExpression.IsMethodReference) { var possibleMethods = m_lambdaLeftExpression.GetMethods().ToArray(); var methods = new List <PrivateMethodInfoForLambdaResolver>(); var instance = m_lambdaLeftExpression.ExpressionCode; // Find methods where this parameter is a delegate that have the right number of parameters. foreach (var method in possibleMethods) { bool matching = false; bool isExtension = method.IsExtension(); var parametersToSkip = (isExtension ? 1 : 0); var parameters = method.GetParameters(); var parTypes = new List <Type>(); if (parameters.Length > parametersToSkip && ImplicitAttribute.IsImplicit(parameters[parametersToSkip])) { parametersToSkip++; // Skip this argument because it is implicit. } var parameterIndex = m_argumentIndex + parametersToSkip; var resolverMethodData = new PrivateMethodInfoForLambdaResolver(method, parameterIndex); if (parameterIndex < parameters.Length) { var parameter = parameters[parameterIndex]; var targetType = parameter.ParameterType; if (targetType.IsDelegate()) { var invokeMethod = targetType.GetMethod("Invoke"); var targetParameters = invokeMethod.GetParameters(); var returnType = invokeMethod.ReturnType; resolverMethodData.DelegateReturnType = returnType; if (parameterNames.Length == targetParameters.Length) { matching = true; if (targetType.HasGenericArguments()) // Only possible if at the same time is an extension method. (!!) { var mga = method.GetGenericArguments().Select(t => new Tuple <string, Type>(t.Name, t)).ToList(); resolverMethodData.MethodGenericArguments = mga; // Resolve those generic arguments from the instance. var typedArgs = instance.Type.GetTypedGenericArguments(parameters[0].ParameterType); foreach (var ti in typedArgs) { var index = mga.FindIndex(t => t.Item1 == ti.Item1); mga[index] = new Tuple <string, Type>(mga[index].Item1, ti.Item2); } // Get types of delegate input parameters. for (int i = 0; i < parameterNames.Length; i++) { var t = targetParameters[i].ParameterType; if (t.IsGenericParameter) { var index = mga.FindIndex(tt => tt.Item1 == t.Name); if (index >= 0) { t = mga[index].Item2; } else { // The type of this input parameter was not found. matching = false; break; } } parTypes.Add(t); } if (matching && returnType.IsGenericParameter) { var nUnknownTypes = mga.Count(tt => tt.Item2.IsGenericParameter); if (nUnknownTypes > 1) { matching = false; } else if (nUnknownTypes == 1) { matching = mga.Exists(tt => tt.Item2 == returnType); } } resolverMethodData.LambdaParameterTypes = parTypes; } else { resolverMethodData.LambdaParameterTypes = parameterNames.Select((n, i) => targetParameters[i].ParameterType).ToList(); resolverMethodData.DelegateType = targetType; } } } } if (matching) { methods.Add(resolverMethodData); } else { m_lambdaLeftExpression.RemoveMethod(method); // Remove the method as a possible match, to avoid further processing. } } if (methods.Count == 1) { var methodData = methods[0]; var method = methodData.Method; var scope = new ProcedureParsingScope(m_scopeStack.Peek(), "Lambda", ProcedureParsingScope.ScopeType.Lambda); m_scopeStack.Push(scope); for (int i = 0; i < parameterNames.Length; i++) { scope.AddLambdaExpressionParameter(methodData.LambdaParameterTypes[i], parameterNames[i]); } m_lambdaDelegateTargetType = methodData.DelegateType; m_lambdaDelegateReturnType = methodData.DelegateReturnType; m_lambdaDelegateGenericArguments = methodData.MethodGenericArguments; } else { throw new NotImplementedException("Having more than one matching methods is not supported. Maybe it never will, and this should just be a parsing error."); } } else { throw new NotImplementedException(); } }
internal int CheckMethodArguments( ref MethodInfo method, bool isProcedure, bool firstParIsThis, Expression instance, Expression contextReference, Expression extensionInstance, List <SBExpressionData> arguments, List <SBExpressionData> suggestedAssignmentsOut, PropertyBlock propertyBlock = null) { ListElementPicker <SBExpressionData> argPicker = new ListElementPicker <SBExpressionData>(arguments); ArgumentInsertState state = ArgumentInsertState.Initial; bool thisParameterHandled = false; int matchScore = 1000; if (isProcedure) { state = ArgumentInsertState.ProcedureContext; } else if (extensionInstance != null) { state = ArgumentInsertState.ExtensionInstance; } // Construct method if generic and extension method. if (method.IsGenericMethodDefinition && extensionInstance != null) { // NOTE: This has to be improved with checks to avoid mistakes. var t = extensionInstance.Type; if (extensionInstance.Type.IsConstructedGenericType) { t = t.GetGenericArguments()[0]; } method = method.MakeGenericMethod(t); } var parameters = method.GetParameters(); // Run through all the parameters; all must be assigned. for (int i = 0; i < parameters.Length; i++) { var p = parameters[i]; if (state == ArgumentInsertState.ExtensionInstance) { suggestedAssignmentsOut.Add(new SBExpressionData(extensionInstance)); state = ArgumentInsertState.Initial; continue; } else if (state == ArgumentInsertState.ProcedureContext) { suggestedAssignmentsOut.Add(new SBExpressionData(contextReference)); state = ArgumentInsertState.Initial; continue; } else if (state == ArgumentInsertState.Initial) { if (ImplicitAttribute.IsImplicit(p)) { if (p.ParameterType.IsAssignableFrom(typeof(ICallContext))) { if (isProcedure) { suggestedAssignmentsOut.Add(new SBExpressionData(m_currentProcedure.ContextReferenceInternal)); } else { suggestedAssignmentsOut.Add(new SBExpressionData(m_currentProcedure.ContextReference)); } continue; } else { // TODO: Parsing error. throw new NotImplementedException(); } } else if (!thisParameterHandled && firstParIsThis) { state = ArgumentInsertState.ThisReference; } else { state = ArgumentInsertState.Mandatory; } } if (state != ArgumentInsertState.ThisReference) { if (argPicker.UnpickedCount == 0 && state != ArgumentInsertState.ThisReference) { state = ArgumentInsertState.NoArguments; } else if (p.IsDefined(typeof(ParamArrayAttribute))) { state = ArgumentInsertState.Params; } } if (state == ArgumentInsertState.Mandatory) { if (p.GetType() == typeof(ArgumentList)) { state = ArgumentInsertState.ArgumentList; } else { if (argPicker.Current.IsNamed) { if (argPicker.AllBeforeCurrentArePickedAndOthersUnpicked()) { state = ArgumentInsertState.Named; } else { // TODO: report error; unexpected that args before current are not picked. return(0); } } else if (IsParameterAssignableFromArgument(p, argPicker.Current)) { var a = argPicker.Pick(); if (p.ParameterType == typeof(object)) { a = a.NewExpressionCode(Expression.Convert(a.ExpressionCode, typeof(object))); matchScore -= 10; // Matching an object parameter is not as good as matching the exact same type. } suggestedAssignmentsOut.Add(a); continue; // next parameter } else if (argPicker.Current.DataType.Type == typeof(Int64) && p.ParameterType.IsPrimitiveNarrowableIntType()) { suggestedAssignmentsOut.Add(new SBExpressionData(Expression.Convert(argPicker.Pick().ExpressionCode, p.ParameterType))); continue; // next parameter } else if (argPicker.Current.DataType.Type == typeof(Double) && p.ParameterType == typeof(Single)) { suggestedAssignmentsOut.Add(new SBExpressionData(Expression.Convert(argPicker.Pick().ExpressionCode, p.ParameterType))); matchScore -= 5; // Matching a 'single' is not as good as matching the exact same type. continue; // next parameter } else { state = ArgumentInsertState.Named; // Try finding the argument by name, then. } } } if (state == ArgumentInsertState.ThisReference) { thisParameterHandled = true; if (p.ParameterType.IsAssignableFrom(instance.Type)) { suggestedAssignmentsOut.Add(new SBExpressionData(instance)); state = ArgumentInsertState.Mandatory; // Not sure about this... } else { return(0); // Wrong type of 'this' reference } } if (state == ArgumentInsertState.Named) { // Note: Just because the call uses named arguments does not mean that the parameters have default values. if (argPicker.FindUnpicked(a => a.ParameterName == p.Name)) { suggestedAssignmentsOut.Add(argPicker.Pick()); continue; // next parameter } else { if (p.HasDefaultValue) { suggestedAssignmentsOut.Add( new SBExpressionData( Expression.Constant(p.DefaultValue, p.ParameterType))); continue; } else { // TODO: Report argument not found, maybe return(0); } } } if (state == ArgumentInsertState.Params) { if (!p.ParameterType.IsArray) { throw new Exception("Unexpected type of 'params' parameter; not an array."); } var t = p.ParameterType.GetElementType(); if (!argPicker.AllBeforeCurrentArePickedAndOthersUnpicked()) { // TODO: report error, maybe return(0); } if (argPicker.UnpickedCount == 1 && p.ParameterType.IsAssignableFrom(argPicker.Current.DataType.Type)) { suggestedAssignmentsOut.Add(argPicker.Pick()); continue; } else { var paramsArgs = new List <Expression>(); while (argPicker.UnpickedCount > 0) { if (t == argPicker.Current.DataType.Type) { paramsArgs.Add(argPicker.Pick().ExpressionCode); } else if (t.IsAssignableFrom(argPicker.Current.DataType.Type)) { paramsArgs.Add(Expression.Convert(argPicker.Pick().ExpressionCode, t)); } else { // TODO: report error, maybe return(0); } } suggestedAssignmentsOut.Add(new SBExpressionData(Expression.NewArrayInit(t, paramsArgs))); } continue; } if (state == ArgumentInsertState.NoArguments) { if (p.IsDefined(typeof(ParamArrayAttribute))) { if (!p.ParameterType.IsArray) { throw new Exception("Unexpected type of 'params' parameter; not an array."); } var t = p.ParameterType.GetElementType(); suggestedAssignmentsOut.Add(new SBExpressionData(Expression.NewArrayInit(t))); } else { if (p.HasDefaultValue) { object c = p.DefaultValue; if (c == null && p.ParameterType.IsValueType) { c = Activator.CreateInstance(p.ParameterType); } suggestedAssignmentsOut.Add( new SBExpressionData( Expression.Constant(c, p.ParameterType))); continue; } else { // TODO: Report argument not found, maybe return(0); } } } } if (suggestedAssignmentsOut.Count >= parameters.Length) { if (argPicker.PickedCount < arguments.Count) { throw new Exception("Not all passed arguments are used!"); // TODO: report in a better way. } return(matchScore); } return(0); }