MethodInfoFunctionInstance.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Linq;
  5. using System.Reflection;
  6. using Jint.Native;
  7. using Jint.Native.Array;
  8. using Jint.Native.Function;
  9. namespace Jint.Runtime.Interop
  10. {
  11. public sealed class MethodInfoFunctionInstance : FunctionInstance
  12. {
  13. private readonly MethodInfo[] _methods;
  14. public MethodInfoFunctionInstance(Engine engine, MethodInfo[] methods)
  15. : base(engine, null, null, false)
  16. {
  17. _methods = methods;
  18. Prototype = engine.Function.PrototypeObject;
  19. }
  20. public override JsValue Call(JsValue thisObject, JsValue[] arguments)
  21. {
  22. return Invoke(_methods, thisObject, arguments);
  23. }
  24. public JsValue Invoke(MethodInfo[] methodInfos, JsValue thisObject, JsValue[] jsArguments)
  25. {
  26. var arguments = ProcessParamsArrays(jsArguments, methodInfos);
  27. var methods = TypeConverter.FindBestMatch(Engine, methodInfos, arguments).ToList();
  28. var converter = Engine.ClrTypeConverter;
  29. foreach (var method in methods)
  30. {
  31. var parameters = new object[arguments.Length];
  32. var argumentsMatch = true;
  33. for (var i = 0; i < arguments.Length; i++)
  34. {
  35. var parameterType = method.GetParameters()[i].ParameterType;
  36. if (parameterType == typeof(JsValue))
  37. {
  38. parameters[i] = arguments[i];
  39. }
  40. else
  41. {
  42. if (!converter.TryConvert(arguments[i].ToObject(), parameterType, CultureInfo.InvariantCulture, out parameters[i]))
  43. {
  44. argumentsMatch = false;
  45. break;
  46. }
  47. if (typeof(System.Linq.Expressions.LambdaExpression).IsAssignableFrom(parameters[i].GetType()))
  48. {
  49. parameters[i] = (parameters[i] as System.Linq.Expressions.LambdaExpression).Compile();
  50. }
  51. }
  52. }
  53. if (!argumentsMatch)
  54. {
  55. continue;
  56. }
  57. // todo: cache method info
  58. return JsValue.FromObject(Engine, method.Invoke(thisObject.ToObject(), parameters.ToArray()));
  59. }
  60. throw new JavaScriptException(Engine.TypeError, "No public methods with the specified arguments were found.");
  61. }
  62. private JsValue[] ProcessParamsArrays(JsValue[] jsArguments, IEnumerable<MethodInfo> methodInfos)
  63. {
  64. foreach (var methodInfo in methodInfos)
  65. {
  66. var parameters = methodInfo.GetParameters();
  67. if (!parameters.Any(p => Attribute.IsDefined(p, typeof(ParamArrayAttribute))))
  68. continue;
  69. var nonParamsArgumentsCount = parameters.Length - 1;
  70. if (jsArguments.Length < nonParamsArgumentsCount)
  71. continue;
  72. var newArgumentsCollection = jsArguments.Take(nonParamsArgumentsCount).ToList();
  73. var argsToTransform = jsArguments.Skip(nonParamsArgumentsCount).ToList();
  74. if (argsToTransform.Count == 1 && argsToTransform.FirstOrDefault().IsArray())
  75. continue;
  76. var arrayInstance = ArrayConstructor.CreateArrayConstructor(Engine).Construct(argsToTransform.ToArray());
  77. newArgumentsCollection.Add(new JsValue(arrayInstance));
  78. return newArgumentsCollection.ToArray();
  79. }
  80. return jsArguments;
  81. }
  82. }
  83. }