MethodDescriptor.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. using Jint.Native;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Reflection;
  5. using System.Runtime.CompilerServices;
  6. using Jint.Extensions;
  7. namespace Jint.Runtime.Interop
  8. {
  9. internal sealed class MethodDescriptor
  10. {
  11. internal MethodDescriptor(MethodBase method)
  12. {
  13. Method = method;
  14. Parameters = method.GetParameters();
  15. IsExtensionMethod = method.IsDefined(typeof(ExtensionAttribute), true);
  16. foreach (var parameter in Parameters)
  17. {
  18. if (Attribute.IsDefined(parameter, typeof(ParamArrayAttribute)))
  19. {
  20. HasParams = true;
  21. break;
  22. }
  23. if (parameter.HasDefaultValue)
  24. {
  25. ParameterDefaultValuesCount++;
  26. }
  27. }
  28. }
  29. public MethodBase Method { get; }
  30. public ParameterInfo[] Parameters { get; }
  31. public bool HasParams { get; }
  32. public int ParameterDefaultValuesCount { get; }
  33. public bool IsExtensionMethod { get; }
  34. public static MethodDescriptor[] Build<T>(List<T> source) where T : MethodBase
  35. {
  36. var descriptors = new MethodDescriptor[source.Count];
  37. for (var i = 0; i < source.Count; i++)
  38. {
  39. descriptors[i] = new MethodDescriptor(source[i]);
  40. }
  41. return Prioritize(descriptors);
  42. }
  43. public static MethodDescriptor[] Build<T>(T[] source) where T : MethodBase
  44. {
  45. var descriptors = new MethodDescriptor[source.Length];
  46. for (var i = 0; i < source.Length; i++)
  47. {
  48. descriptors[i] = new MethodDescriptor(source[i]);
  49. }
  50. return Prioritize(descriptors);
  51. }
  52. private static MethodDescriptor[] Prioritize(MethodDescriptor[] descriptors)
  53. {
  54. static int CreateComparison(MethodDescriptor d1, MethodDescriptor d2)
  55. {
  56. // if its a generic method, put it on the end
  57. if (d1.Method.IsGenericMethod && !d2.Method.IsGenericMethod)
  58. {
  59. return 1;
  60. }
  61. if (d2.Method.IsGenericMethod && !d1.Method.IsGenericMethod)
  62. {
  63. return -1;
  64. }
  65. // put params versions to end, they can be tricky to match and can cause trouble / extra overhead
  66. if (d1.HasParams && !d2.HasParams)
  67. {
  68. return 1;
  69. }
  70. if (d2.HasParams && !d1.HasParams)
  71. {
  72. return -1;
  73. }
  74. // then favor less parameters
  75. if (d1.Parameters.Length > d2.Parameters.Length)
  76. {
  77. return 1;
  78. }
  79. if (d2.Parameters.Length > d1.Parameters.Length)
  80. {
  81. return -1;
  82. }
  83. return 0;
  84. }
  85. Array.Sort(descriptors, CreateComparison);
  86. return descriptors;
  87. }
  88. public JsValue Call(Engine _engine, object instance, JsValue[] arguments)
  89. {
  90. var parameters = new object[arguments.Length];
  91. var methodParameters = Parameters;
  92. var valueCoercionType = _engine.Options.Interop.ValueCoercion;
  93. try
  94. {
  95. for (var i = 0; i < arguments.Length; i++)
  96. {
  97. var parameterType = methodParameters[i].ParameterType;
  98. var value = arguments[i];
  99. object converted;
  100. if (typeof(JsValue).IsAssignableFrom(parameterType))
  101. {
  102. converted = value;
  103. }
  104. else if (!ReflectionExtensions.TryConvertViaTypeCoercion(parameterType, valueCoercionType, value, out converted))
  105. {
  106. converted = _engine.ClrTypeConverter.Convert(
  107. value.ToObject(),
  108. parameterType,
  109. System.Globalization.CultureInfo.InvariantCulture);
  110. }
  111. parameters[i] = converted;
  112. }
  113. if (Method is MethodInfo m)
  114. {
  115. var retVal = m.Invoke(instance, parameters);
  116. return JsValue.FromObject(_engine, retVal);
  117. }
  118. else if (Method is ConstructorInfo c)
  119. {
  120. var retVal = c.Invoke(parameters);
  121. return JsValue.FromObject(_engine, retVal);
  122. }
  123. else
  124. {
  125. throw new Exception("Method is unknown type");
  126. }
  127. }
  128. catch (TargetInvocationException exception)
  129. {
  130. ExceptionHelper.ThrowMeaningfulException(_engine, exception);
  131. return null;
  132. }
  133. }
  134. }
  135. }