private void GenerateCreateMethodCallIL()

in src/Elastic.Apm/Libraries/Newtonsoft.Json/Utilities/DynamicReflectionDelegateFactory.cs [74:234]


		private void GenerateCreateMethodCallIL(MethodBase method, ILGenerator generator, int argsIndex)
		{
			var args = method.GetParameters();

			var argsOk = generator.DefineLabel();

			// throw an error if the number of argument values doesn't match method parameters
			generator.Emit(OpCodes.Ldarg, argsIndex);
			generator.Emit(OpCodes.Ldlen);
			generator.Emit(OpCodes.Ldc_I4, args.Length);
			generator.Emit(OpCodes.Beq, argsOk);
			generator.Emit(OpCodes.Newobj, typeof(TargetParameterCountException).GetConstructor(ReflectionUtils.EmptyTypes));
			generator.Emit(OpCodes.Throw);

			generator.MarkLabel(argsOk);

			if (!method.IsConstructor && !method.IsStatic) generator.PushInstance(method.DeclaringType);

			var localConvertible = generator.DeclareLocal(typeof(IConvertible));
			var localObject = generator.DeclareLocal(typeof(object));

			var variableAddressOpCode = args.Length < 256 ? OpCodes.Ldloca_S : OpCodes.Ldloca;
			var variableLoadOpCode = args.Length < 256 ? OpCodes.Ldloc_S : OpCodes.Ldloc;

			for (var i = 0; i < args.Length; i++)
			{
				var parameter = args[i];
				var parameterType = parameter.ParameterType;

				if (parameterType.IsByRef)
				{
					parameterType = parameterType.GetElementType();

					var localVariable = generator.DeclareLocal(parameterType);

					// don't need to set variable for 'out' parameter
					if (!parameter.IsOut)
					{
						generator.PushArrayInstance(argsIndex, i);

						if (parameterType.IsValueType())
						{
							var skipSettingDefault = generator.DefineLabel();
							var finishedProcessingParameter = generator.DefineLabel();

							// check if parameter is not null
							generator.Emit(OpCodes.Brtrue_S, skipSettingDefault);

							// parameter has no value, initialize to default
							generator.Emit(variableAddressOpCode, localVariable);
							generator.Emit(OpCodes.Initobj, parameterType);
							generator.Emit(OpCodes.Br_S, finishedProcessingParameter);

							// parameter has value, get value from array again and unbox and set to variable
							generator.MarkLabel(skipSettingDefault);
							generator.PushArrayInstance(argsIndex, i);
							generator.UnboxIfNeeded(parameterType);
							generator.Emit(OpCodes.Stloc_S, localVariable);

							// parameter finished, we out!
							generator.MarkLabel(finishedProcessingParameter);
						}
						else
						{
							generator.UnboxIfNeeded(parameterType);
							generator.Emit(OpCodes.Stloc_S, localVariable);
						}
					}

					generator.Emit(variableAddressOpCode, localVariable);
				}
				else if (parameterType.IsValueType())
				{
					generator.PushArrayInstance(argsIndex, i);
					generator.Emit(OpCodes.Stloc_S, localObject);

					// have to check that value type parameters aren't null
					// otherwise they will error when unboxed
					var skipSettingDefault = generator.DefineLabel();
					var finishedProcessingParameter = generator.DefineLabel();

					// check if parameter is not null
					generator.Emit(OpCodes.Ldloc_S, localObject);
					generator.Emit(OpCodes.Brtrue_S, skipSettingDefault);

					// parameter has no value, initialize to default
					var localVariable = generator.DeclareLocal(parameterType);
					generator.Emit(variableAddressOpCode, localVariable);
					generator.Emit(OpCodes.Initobj, parameterType);
					generator.Emit(variableLoadOpCode, localVariable);
					generator.Emit(OpCodes.Br_S, finishedProcessingParameter);

					// argument has value, try to convert it to parameter type
					generator.MarkLabel(skipSettingDefault);

					if (parameterType.IsPrimitive())
					{
						// for primitive types we need to handle type widening (e.g. short -> int)
						var toParameterTypeMethod = typeof(IConvertible)
							.GetMethod("To" + parameterType.Name, new[] { typeof(IFormatProvider) });

						if (toParameterTypeMethod != null)
						{
							var skipConvertible = generator.DefineLabel();

							// check if argument type is an exact match for parameter type
							// in this case we may use cheap unboxing instead
							generator.Emit(OpCodes.Ldloc_S, localObject);
							generator.Emit(OpCodes.Isinst, parameterType);
							generator.Emit(OpCodes.Brtrue_S, skipConvertible);

							// types don't match, check if argument implements IConvertible
							generator.Emit(OpCodes.Ldloc_S, localObject);
							generator.Emit(OpCodes.Isinst, typeof(IConvertible));
							generator.Emit(OpCodes.Stloc_S, localConvertible);
							generator.Emit(OpCodes.Ldloc_S, localConvertible);
							generator.Emit(OpCodes.Brfalse_S, skipConvertible);

							// convert argument to parameter type
							generator.Emit(OpCodes.Ldloc_S, localConvertible);
							generator.Emit(OpCodes.Ldnull);
							generator.Emit(OpCodes.Callvirt, toParameterTypeMethod);
							generator.Emit(OpCodes.Br_S, finishedProcessingParameter);

							generator.MarkLabel(skipConvertible);
						}
					}

					// we got here because either argument type matches parameter (conversion will succeed),
					// or argument type doesn't match parameter, but we're out of options (conversion will fail)
					generator.Emit(OpCodes.Ldloc_S, localObject);

					generator.UnboxIfNeeded(parameterType);

					// parameter finished, we out!
					generator.MarkLabel(finishedProcessingParameter);
				}
				else
				{
					generator.PushArrayInstance(argsIndex, i);

					generator.UnboxIfNeeded(parameterType);
				}
			}

			if (method.IsConstructor)
				generator.Emit(OpCodes.Newobj, (ConstructorInfo)method);
			else
				generator.CallMethod((MethodInfo)method);

			var returnType = method.IsConstructor
				? method.DeclaringType
				: ((MethodInfo)method).ReturnType;

			if (returnType != typeof(void))
				generator.BoxIfNeeded(returnType);
			else
				generator.Emit(OpCodes.Ldnull);

			generator.Return();
		}