in src/profiler/Elastic.Apm.Profiler.Managed/Reflection/MethodBuilder.cs [206:369]
private TDelegate EmitDelegate()
{
var requiresBestEffortMatching = false;
if (_resolutionModule != null)
{
try
{
// Don't resolve until we build, as it may be an unnecessary lookup because of the cache
// We also may need the generics which were specified
if (_forceMethodDefResolve || (_declaringTypeGenerics == null && _methodGenerics == null))
{
_methodBase =
_resolutionModule.ResolveMethod(metadataToken: _mdToken);
}
else
{
_methodBase =
_resolutionModule.ResolveMethod(
metadataToken: _mdToken,
genericTypeArguments: _declaringTypeGenerics,
genericMethodArguments: _methodGenerics);
}
}
catch (Exception ex)
{
Logger.Log(LogLevel.Error, ex, "Unable to resolve method {0}.{1} by metadata token: {2}",
_concreteType,
_methodName,
_mdToken);
requiresBestEffortMatching = true;
}
}
else
Logger.Warn("Unable to resolve module version id {0}. Using method builder fallback.", _moduleVersionId);
MethodInfo methodInfo = null;
if (!requiresBestEffortMatching && _methodBase is MethodInfo info)
{
if (info.IsGenericMethodDefinition)
info = MakeGenericMethod(info);
methodInfo = VerifyMethodFromToken(info);
}
if (methodInfo == null && ForceMdTokenLookup)
{
throw new Exception(
$"Unable to resolve method {_concreteTypeName}.{_methodName} by metadata token: {_mdToken}. Exiting because {nameof(ForceMdTokenLookup)}() is true.");
}
else if (methodInfo == null || ForceFallbackLookup)
{
// mdToken didn't work out, fallback
methodInfo = TryFindMethod();
}
var delegateType = typeof(TDelegate);
var delegateGenericArgs = delegateType.GenericTypeArguments;
Type[] delegateParameterTypes;
Type returnType;
if (delegateType.Name.StartsWith("Func`"))
{
// last generic type argument is the return type
var parameterCount = delegateGenericArgs.Length - 1;
delegateParameterTypes = new Type[parameterCount];
Array.Copy(delegateGenericArgs, delegateParameterTypes, parameterCount);
returnType = delegateGenericArgs[parameterCount];
}
else if (delegateType.Name.StartsWith("Action`"))
{
delegateParameterTypes = delegateGenericArgs;
returnType = typeof(void);
}
else
throw new Exception($"Only Func<> or Action<> are supported in {nameof(MethodBuilder)}.");
if (methodInfo.IsGenericMethodDefinition)
methodInfo = MakeGenericMethod(methodInfo);
Type[] effectiveParameterTypes;
var reflectedParameterTypes =
methodInfo.GetParameters().Select(p => p.ParameterType);
if (methodInfo.IsStatic)
effectiveParameterTypes = reflectedParameterTypes.ToArray();
else
{
// for instance methods, insert object's type as first element in array
effectiveParameterTypes = new[] { _concreteType }
.Concat(reflectedParameterTypes)
.ToArray();
}
var dynamicMethod = new DynamicMethod(methodInfo.Name, returnType, delegateParameterTypes, ObjectExtensions.Module, skipVisibility: true);
var il = dynamicMethod.GetILGenerator();
// load each argument and cast or unbox as necessary
for (ushort argumentIndex = 0; argumentIndex < delegateParameterTypes.Length; argumentIndex++)
{
var delegateParameterType = delegateParameterTypes[argumentIndex];
var underlyingParameterType = effectiveParameterTypes[argumentIndex];
switch (argumentIndex)
{
case 0:
il.Emit(OpCodes.Ldarg_0);
break;
case 1:
il.Emit(OpCodes.Ldarg_1);
break;
case 2:
il.Emit(OpCodes.Ldarg_2);
break;
case 3:
il.Emit(OpCodes.Ldarg_3);
break;
default:
il.Emit(OpCodes.Ldarg_S, argumentIndex);
break;
}
if (underlyingParameterType.IsValueType && delegateParameterType == typeof(object))
il.Emit(OpCodes.Unbox_Any, underlyingParameterType);
else if (underlyingParameterType != delegateParameterType)
il.Emit(OpCodes.Castclass, underlyingParameterType);
}
if (_opCode == OpCodeValue.Call || methodInfo.IsStatic)
{
// non-virtual call (e.g. static method, or method override calling overriden implementation)
il.Emit(OpCodes.Call, methodInfo);
}
else if (_opCode == OpCodeValue.Callvirt)
{
// Note: C# compiler uses CALLVIRT for non-virtual
// instance methods to get the cheap null check
il.Emit(OpCodes.Callvirt, methodInfo);
}
else
throw new NotSupportedException($"OpCode {_originalOpCodeValue} not supported when calling a method.");
if (methodInfo.ReturnType.IsValueType && !returnType.IsValueType)
il.Emit(OpCodes.Box, methodInfo.ReturnType);
else if (methodInfo.ReturnType.IsValueType && returnType.IsValueType && methodInfo.ReturnType != returnType)
{
throw new ArgumentException(
$"Cannot convert the target method's return type {methodInfo.ReturnType.FullName} (value type) to the delegate method's return type {returnType.FullName} (value type)");
}
else if (!methodInfo.ReturnType.IsValueType && returnType.IsValueType)
{
throw new ArgumentException(
$"Cannot reliably convert the target method's return type {methodInfo.ReturnType.FullName} (reference type) to the delegate method's return type {returnType.FullName} (value type)");
}
else if (!methodInfo.ReturnType.IsValueType && !returnType.IsValueType && methodInfo.ReturnType != returnType)
il.Emit(OpCodes.Castclass, returnType);
il.Emit(OpCodes.Ret);
return (TDelegate)dynamicMethod.CreateDelegate(typeof(TDelegate));
}