123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- using Jint.Native;
- using System.Reflection;
- using System.Runtime.CompilerServices;
- using Jint.Extensions;
- #pragma warning disable IL2072
- namespace Jint.Runtime.Interop;
- internal sealed class MethodDescriptor
- {
- internal MethodDescriptor(MethodBase method)
- {
- Method = method;
- Parameters = method.GetParameters();
- IsExtensionMethod = method.IsDefined(typeof(ExtensionAttribute), true);
- foreach (var parameter in Parameters)
- {
- if (Attribute.IsDefined(parameter, typeof(ParamArrayAttribute)))
- {
- HasParams = true;
- break;
- }
- if (parameter.HasDefaultValue)
- {
- ParameterDefaultValuesCount++;
- }
- }
- }
- public MethodBase Method { get; }
- public ParameterInfo[] Parameters { get; }
- public bool HasParams { get; }
- public int ParameterDefaultValuesCount { get; }
- public bool IsExtensionMethod { get; }
- public static MethodDescriptor[] Build<T>(List<T> source) where T : MethodBase
- {
- var descriptors = new MethodDescriptor[source.Count];
- for (var i = 0; i < source.Count; i++)
- {
- descriptors[i] = new MethodDescriptor(source[i]);
- }
- return Prioritize(descriptors);
- }
- public static MethodDescriptor[] Build<T>(T[] source) where T : MethodBase
- {
- var descriptors = new MethodDescriptor[source.Length];
- for (var i = 0; i < source.Length; i++)
- {
- descriptors[i] = new MethodDescriptor(source[i]);
- }
- return Prioritize(descriptors);
- }
- private static MethodDescriptor[] Prioritize(MethodDescriptor[] descriptors)
- {
- static int CreateComparison(MethodDescriptor d1, MethodDescriptor d2)
- {
- // if its a generic method, put it on the end
- if (d1.Method.IsGenericMethod && !d2.Method.IsGenericMethod)
- {
- return 1;
- }
- if (d2.Method.IsGenericMethod && !d1.Method.IsGenericMethod)
- {
- return -1;
- }
- // put params versions to end, they can be tricky to match and can cause trouble / extra overhead
- if (d1.HasParams && !d2.HasParams)
- {
- return 1;
- }
- if (d2.HasParams && !d1.HasParams)
- {
- return -1;
- }
- // then favor less parameters
- if (d1.Parameters.Length > d2.Parameters.Length)
- {
- return 1;
- }
- if (d2.Parameters.Length > d1.Parameters.Length)
- {
- return -1;
- }
- return 0;
- }
- Array.Sort(descriptors, CreateComparison);
- return descriptors;
- }
- public JsValue Call(Engine engine, object? instance, JsCallArguments arguments)
- {
- var parameters = new object?[arguments.Length];
- var methodParameters = Parameters;
- var valueCoercionType = engine.Options.Interop.ValueCoercion;
- try
- {
- for (var i = 0; i < arguments.Length; i++)
- {
- var methodParameter = methodParameters[i];
- var parameterType = methodParameter.ParameterType;
- var value = arguments[i];
- object? converted;
- if (typeof(JsValue).IsAssignableFrom(parameterType))
- {
- converted = value;
- }
- else if (value.IsUndefined() && methodParameter.IsOptional)
- {
- // undefined is considered missing, null is considered explicit value
- converted = methodParameter.DefaultValue;
- }
- else if (!ReflectionExtensions.TryConvertViaTypeCoercion(parameterType, valueCoercionType, value, out converted))
- {
- converted = engine.TypeConverter.Convert(
- value.ToObject(),
- parameterType,
- System.Globalization.CultureInfo.InvariantCulture);
- }
- parameters[i] = converted;
- }
- var retVal = Method switch
- {
- MethodInfo m => m.Invoke(instance, parameters),
- ConstructorInfo c => c.Invoke(parameters),
- _ => throw new NotSupportedException("Method is unknown type"),
- };
- return JsValue.FromObject(engine, retVal);
- }
- catch (TargetInvocationException exception)
- {
- Throw.MeaningfulException(engine, exception);
- return null;
- }
- }
- }
|