IEnumerable <MethodDataAdapter> GetMethodCandidates(IInvocation invocation) { var abstractionType = ((DispatchedObject)invocation.Proxy).AbstractionType; var implementationType = Node.Mappings[abstractionType]; var methodInfoAdapter = _methodInfoAdapters[invocation.Method]; var bindingFlags = methodInfoAdapter.IsStatic ? BindingFlags.Public | BindingFlags.Static : BindingFlags.Public | BindingFlags.Instance; var candidateMethods = methodInfoAdapter.IsConstructor ? implementationType.GetConstructors(bindingFlags) : (IEnumerable <MethodBase>)implementationType .GetMethods(bindingFlags) .Where(m => methodInfoAdapter.AliasNames == null && invocation.Method.Name.Equals(m.Name) || methodInfoAdapter.AliasNames != null && methodInfoAdapter.AliasNames.Contains(m.Name)); var abstractionParameters = invocation.Method.GetParameters(); foreach (var candidate in candidateMethods) { var implementationParameters = candidate.GetParameters(); if (abstractionParameters.Length != implementationParameters.Length) { continue; } var parameterAdapters = new List <ITypeAdapter>(); var parameterAdaptable = true; for (var i = 0; i < abstractionParameters.Length; i++) { var parameterAdapter = TypeAdapter.Build(Node.Mappings, abstractionParameters[i].ParameterType, implementationParameters[i].ParameterType); if (parameterAdapter == null) { parameterAdaptable = false; break; } parameterAdapters.Add(parameterAdapter); } if (!parameterAdaptable) { continue; } var implementationReturnType = candidate is ConstructorInfo c ? c.DeclaringType : ((MethodInfo)candidate).ReturnType; var returnAdapter = TypeAdapter.Build(Node.Mappings, implementationReturnType, invocation.Method.ReturnType); if (returnAdapter != null) { yield return(new MethodDataAdapter(candidate, parameterAdapters, returnAdapter)); } } }
void MakeHandler(IInvocation invocation) { var abstractionType = ((DispatchedObject)invocation.Proxy).AbstractionType; var implementationType = Node.Mappings[abstractionType]; var candidates = this.GetMethodCandidates(invocation).ToArray(); if (candidates.Length == 0) { _knownHandlers.Add(invocation.Method, i => throw new MethodNotImplementedException(i.Method, implementationType)); return; } if (candidates.Length > 1) { _knownHandlers.Add(invocation.Method, i => throw new AmbiguousMethodException(candidates.Select(c => c.Method))); return; } var candidate = candidates.Single(); var variables = new List <ParameterExpression>(); var body = new List <Expression>(); var byRefAssignBack = new List <Expression>(); var invocationDefinition = Expression.Parameter(typeof(IInvocation)); var invocationArguments = Expression.MakeMemberAccess(invocationDefinition, Ref.IInvocation_Arguments); for (var i = 0; i < candidate.ParameterAdapters.Length; i++) { var adapter = candidate.ParameterAdapters[i]; var byRef = adapter.To.IsByRef; var variableType = byRef ? adapter.To.GetElementType() : adapter.To; var variable = Expression.Variable(variableType); var assignVariable = Expression.Assign( variable, Expression.Convert( Expression.Call( Expression.Constant(adapter), Ref.ITypeAdapter_Adapt, Expression.ArrayAccess(invocationArguments, Expression.Constant(i)), Expression.Constant(this)), variableType)); variables.Add(variable); body.Add(assignVariable); if (byRef) { var assignBackAdapter = TypeAdapter.Build(Node.Mappings, variableType, adapter.From.GetElementType()); byRefAssignBack.Add( Expression.Assign( Expression.ArrayAccess(invocationArguments, Expression.Constant(i)), Expression.Convert( Expression.Call( Expression.Constant(assignBackAdapter), Ref.ITypeAdapter_Adapt, variable, Expression.Constant(this)), typeof(object)))); } } Expression invocationReturnValue = Expression.MakeMemberAccess(invocationDefinition, Ref.IInvocation_ReturnValue); if (candidate.Method is ConstructorInfo ctor) { body.Add( Expression.Assign( invocationReturnValue, Expression.Convert( Expression.Call( Expression.Constant(candidate.ReturnAdapter), Ref.ITypeAdapter_Adapt, Expression.New(ctor, variables), Expression.Constant(this)), typeof(object)))); } else if (candidate.Method is MethodInfo method) { Expression invokeMethod = method.IsStatic ? Expression.Call(method, variables) : Expression.Call( Expression.Convert( Expression.MakeMemberAccess( Expression.Convert( Expression.MakeMemberAccess(invocationDefinition, Ref.IInvocation_Proxy), typeof(DispatchedObject)), Ref.DispatchedObject_Implementation), implementationType), method, variables); if (method.ReturnType == typeof(void)) { body.Add(invokeMethod); } else { body.Add( Expression.Assign( invocationReturnValue, Expression.Convert( Expression.Call( Expression.Constant(candidate.ReturnAdapter), Ref.ITypeAdapter_Adapt, Expression.Convert(invokeMethod, typeof(object)), Expression.Constant(this)), typeof(object)))); } } if (byRefAssignBack.Any()) { body.AddRange(byRefAssignBack); } var handler = Expression.Lambda <Action <IInvocation> >(WrapExceptions(Expression.Block(variables, body)), invocationDefinition); _knownHandlers.Add(invocation.Method, handler.Compile()); }