private TDelegate EmitDelegate()

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));
		}