in src/DotNetWorker.Core/Invocation/DefaultMethodInvokerFactory.cs [17:189]
public IMethodInvoker<TInstance, TReturn> Create<TInstance, TReturn>(MethodInfo method)
{
if (method == null)
{
throw new ArgumentNullException(nameof(method));
}
if (typeof(TInstance) != method.ReflectedType)
{
throw new InvalidOperationException("The Type must match the method's ReflectedType.");
}
// Parameter to invoker: TInstance instance
ParameterExpression instanceParameter = Expression.Parameter(typeof(TInstance), "instance");
// Parameter to invoker: object[] arguments
ParameterExpression argumentsParameter = Expression.Parameter(typeof(object[]), "arguments");
// Local variables passed as arguments to Call
List<ParameterExpression> localVariables = new List<ParameterExpression>();
// Pre-Call, copy from arguments array to local variables.
List<Expression> arrayToLocalsAssignments = new List<Expression>();
// Post-Call, copy from local variables back to arguments array.
List<Expression> localsToArrayAssignments = new List<Expression>();
// If the method returns a value: T returnValue
ParameterExpression returnValue;
Type returnType = method.ReturnType;
if (returnType == typeof(void))
{
returnValue = null;
}
else
{
returnValue = Expression.Parameter(returnType);
}
ParameterInfo[] parameterInfos = method.GetParameters();
Debug.Assert(parameterInfos != null);
for (int index = 0; index < parameterInfos.Length; index++)
{
ParameterInfo parameterInfo = parameterInfos[index];
Type argumentType = parameterInfo.ParameterType;
if (argumentType.IsByRef)
{
// The type of the local variable (and object in the arguments array) should be T rather than T&.
argumentType = argumentType.GetElementType()!;
}
// T argumentN
ParameterExpression localVariable = Expression.Parameter(argumentType);
localVariables.Add(localVariable);
// arguments[index]
Expression arrayAccess = Expression.ArrayAccess(argumentsParameter, Expression.Constant(index));
// Pre-Call:
// T argumentN = (T)arguments[index];
Expression arrayAccessAsT = Expression.Convert(arrayAccess, argumentType);
Expression assignArrayToLocal = Expression.Assign(localVariable, arrayAccessAsT);
arrayToLocalsAssignments.Add(assignArrayToLocal);
// Post-Call:
// arguments[index] = (object)argumentN;
Expression localAsObject = Expression.Convert(localVariable, typeof(object));
Expression assignLocalToArray = Expression.Assign(arrayAccess, localAsObject);
localsToArrayAssignments.Add(assignLocalToArray);
}
Expression callInstance;
if (method.IsStatic)
{
callInstance = null;
}
else
{
callInstance = instanceParameter;
}
// Instance call:
// instance.method(param0, param1, ...);
// Static call:
// method(param0, param1, ...);
Expression call = Expression.Call(callInstance, method, localVariables);
Expression callResult;
if (returnType == typeof(void))
{
callResult = call;
}
else
{
// T returnValue = method(param0, param1, ...);
callResult = Expression.Assign(returnValue, call);
}
List<Expression> blockExpressions = new List<Expression>();
// T0 argument0 = (T0)arguments[0];
// T1 argument1 = (T1)arguments[1];
// ...
blockExpressions.AddRange(arrayToLocalsAssignments);
// Call(argument0, argument1, ...);
// or
// T returnValue = Call(param0, param1, ...);
blockExpressions.Add(callResult);
// arguments[0] = (object)argument0;
// arguments[1] = (object)argument1;
// ...
blockExpressions.AddRange(localsToArrayAssignments);
if (returnValue != null)
{
// return returnValue;
blockExpressions.Add(returnValue);
}
List<ParameterExpression> blockVariables = new List<ParameterExpression>();
blockVariables.AddRange(localVariables);
if (returnValue != null)
{
blockVariables.Add(returnValue);
}
Expression block = Expression.Block(blockVariables, blockExpressions);
if (call.Type == typeof(void))
{
// for: public void Function()
var lambda = Expression.Lambda<Action<TInstance, object[]>>(
block,
instanceParameter,
argumentsParameter);
Action<TInstance, object[]> compiled = lambda.Compile();
return new VoidMethodInvoker<TInstance, TReturn>(compiled);
}
else if (call.Type == typeof(Task))
{
// for: public Task Function()
var lambda = Expression.Lambda<Func<TInstance, object[], Task>>(
block,
instanceParameter,
argumentsParameter);
Func<TInstance, object[], Task> compiled = lambda.Compile();
return new VoidTaskMethodInvoker<TInstance, TReturn>(compiled);
}
else if (typeof(Task).IsAssignableFrom(call.Type))
{
// for: public Task<TReturn> Function()
var lambda = Expression.Lambda<Func<TInstance, object[], Task<TReturn>>>(
block,
instanceParameter,
argumentsParameter);
Func<TInstance, object[], Task<TReturn>> compiled = lambda.Compile();
return new TaskMethodInvoker<TInstance, TReturn>(compiled);
}
else
{
// for: public TReturn Function()
var lambda = Expression.Lambda<Func<TInstance, object[], TReturn>>(
block,
instanceParameter,
argumentsParameter);
Func<TInstance, object[], TReturn> compiled = lambda.Compile();
return new MethodInvokerWithReturnValue<TInstance, TReturn>(compiled);
}
}