DelegateWrapper.cs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. using System;
  2. using System.Globalization;
  3. using System.Reflection;
  4. using Jint.Native;
  5. using Jint.Native.Function;
  6. namespace Jint.Runtime.Interop
  7. {
  8. /// <summary>
  9. /// Represents a FunctionInstance wrapper around a CLR method. This is used by user to pass
  10. /// custom methods to the engine.
  11. /// </summary>
  12. public sealed class DelegateWrapper : FunctionInstance
  13. {
  14. private readonly Delegate _d;
  15. private readonly bool _delegateContainsParamsArgument;
  16. public DelegateWrapper(Engine engine, Delegate d) : base(engine, null, null, false)
  17. {
  18. _d = d;
  19. Prototype = engine.Function.PrototypeObject;
  20. var parameterInfos = _d.Method.GetParameters();
  21. _delegateContainsParamsArgument = false;
  22. foreach (var p in parameterInfos)
  23. {
  24. if (Attribute.IsDefined(p, typeof(ParamArrayAttribute)))
  25. {
  26. _delegateContainsParamsArgument = true;
  27. break;
  28. }
  29. }
  30. }
  31. public override JsValue Call(JsValue thisObject, JsValue[] jsArguments)
  32. {
  33. var parameterInfos = _d.Method.GetParameters();
  34. int delegateArgumentsCount = parameterInfos.Length;
  35. int delegateNonParamsArgumentsCount = _delegateContainsParamsArgument ? delegateArgumentsCount - 1 : delegateArgumentsCount;
  36. int jsArgumentsCount = jsArguments.Length;
  37. int jsArgumentsWithoutParamsCount = Math.Min(jsArgumentsCount, delegateNonParamsArgumentsCount);
  38. var parameters = new object[delegateArgumentsCount];
  39. // convert non params parameter to expected types
  40. for (var i = 0; i < jsArgumentsWithoutParamsCount; i++)
  41. {
  42. var parameterType = parameterInfos[i].ParameterType;
  43. if (parameterType == typeof (JsValue))
  44. {
  45. parameters[i] = jsArguments[i];
  46. }
  47. else
  48. {
  49. parameters[i] = Engine.ClrTypeConverter.Convert(
  50. jsArguments[i].ToObject(),
  51. parameterType,
  52. CultureInfo.InvariantCulture);
  53. }
  54. }
  55. // assign null to parameters not provided
  56. for (var i = jsArgumentsWithoutParamsCount; i < delegateNonParamsArgumentsCount; i++)
  57. {
  58. if (parameterInfos[i].ParameterType.IsValueType)
  59. {
  60. parameters[i] = Activator.CreateInstance(parameterInfos[i].ParameterType);
  61. }
  62. else
  63. {
  64. parameters[i] = null;
  65. }
  66. }
  67. // assign params to array and converts each objet to expected type
  68. if(_delegateContainsParamsArgument)
  69. {
  70. int paramsArgumentIndex = delegateArgumentsCount - 1;
  71. int paramsCount = Math.Max(0, jsArgumentsCount - delegateNonParamsArgumentsCount);
  72. object[] paramsParameter = new object[paramsCount];
  73. var paramsParameterType = parameterInfos[paramsArgumentIndex].ParameterType.GetElementType();
  74. for (var i = paramsArgumentIndex; i < jsArgumentsCount; i++)
  75. {
  76. var paramsIndex = i - paramsArgumentIndex;
  77. if (paramsParameterType == typeof(JsValue))
  78. {
  79. paramsParameter[paramsIndex] = jsArguments[i];
  80. }
  81. else
  82. {
  83. paramsParameter[paramsIndex] = Engine.ClrTypeConverter.Convert(
  84. jsArguments[i].ToObject(),
  85. paramsParameterType,
  86. CultureInfo.InvariantCulture);
  87. }
  88. }
  89. parameters[paramsArgumentIndex] = paramsParameter;
  90. }
  91. try
  92. {
  93. return JsValue.FromObject(Engine, _d.DynamicInvoke(parameters));
  94. }
  95. catch (TargetInvocationException exception)
  96. {
  97. var meaningfulException = exception.InnerException ?? exception;
  98. var handler = Engine.Options._ClrExceptionsHandler;
  99. if (handler != null && handler(meaningfulException))
  100. {
  101. ExceptionHelper.ThrowError(_engine, meaningfulException.Message);
  102. }
  103. throw meaningfulException;
  104. }
  105. }
  106. }
  107. }