in src/WebJobs.Extensions.DurableTask/EntityScheduler/Proxy/EntityProxyFactory.cs [84:171]
private static void BuildMethods(TypeBuilder typeBuilder, Type interfaceType)
{
var methods = interfaceType.GetMethods(BindingFlags.Instance | BindingFlags.Public).ToList();
// Interfaces can inherit from other interfaces, getting those methods too
var interfaces = interfaceType.GetInterfaces();
if (interfaces.Length > 0)
{
foreach (var inter in interfaces)
{
methods.AddRange(inter.GetMethods(BindingFlags.Instance | BindingFlags.Public));
}
}
if (methods.Count == 0)
{
throw new InvalidOperationException($"Interface '{interfaceType.FullName}' has no methods defined.");
}
var entityProxyMethods = typeof(EntityProxy).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance);
var callAsyncMethod = entityProxyMethods.First(x => x.Name == nameof(EntityProxy.CallAsync) && !x.IsGenericMethod);
var callAsyncGenericMethod = entityProxyMethods.First(x => x.Name == nameof(EntityProxy.CallAsync) && x.IsGenericMethod);
var signalMethod = entityProxyMethods.First(x => x.Name == nameof(EntityProxy.Signal));
foreach (var methodInfo in methods)
{
var parameters = methodInfo.GetParameters();
// check that the number of arguments is zero or one
if (parameters.Length > 1)
{
throw new InvalidOperationException($"Method '{methodInfo.Name}' defines more than one parameter. Entity proxy interface methods must define at most one argument for operation input.");
}
var returnType = methodInfo.ReturnType;
// check that return type is void / Task / Task<T>.
if (returnType != typeof(void) && !(returnType == typeof(Task) || returnType.BaseType == typeof(Task)))
{
throw new InvalidOperationException($"Method '{methodInfo.Name}' has a return type which is neither void nor a Task. Entity proxy interface methods may only return void, Task, or Task<T>.");
}
var proxyMethod = typeBuilder.DefineMethod(
methodInfo.Name,
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.SpecialName | MethodAttributes.Virtual,
returnType,
parameters.Length == 0 ? null : new[] { parameters[0].ParameterType });
typeBuilder.DefineMethodOverride(proxyMethod, methodInfo);
var ilGenerator = proxyMethod.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldstr, methodInfo.Name);
if (parameters.Length == 0)
{
ilGenerator.Emit(OpCodes.Ldnull);
}
else
{
ilGenerator.Emit(OpCodes.Ldarg_1);
// ValueType needs boxing.
if (parameters[0].ParameterType.IsValueType)
{
ilGenerator.Emit(OpCodes.Box, parameters[0].ParameterType);
}
}
if (returnType == typeof(void))
{
ilGenerator.Emit(OpCodes.Call, signalMethod);
}
else
{
ilGenerator.DeclareLocal(returnType);
ilGenerator.Emit(OpCodes.Call, returnType.IsGenericType ? callAsyncGenericMethod.MakeGenericMethod(returnType.GetGenericArguments()[0]) : callAsyncMethod);
ilGenerator.Emit(OpCodes.Stloc_0);
ilGenerator.Emit(OpCodes.Ldloc_0);
}
ilGenerator.Emit(OpCodes.Ret);
}
}