DelegateWrapper.cs 5.0 KB

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