DelegateWrapper.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  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)
  17. : base(engine, "delegate", null, null, false)
  18. {
  19. _d = d;
  20. _prototype = engine.Function.PrototypeObject;
  21. var parameterInfos = _d.Method.GetParameters();
  22. _delegateContainsParamsArgument = false;
  23. foreach (var p in parameterInfos)
  24. {
  25. if (Attribute.IsDefined(p, typeof(ParamArrayAttribute)))
  26. {
  27. _delegateContainsParamsArgument = true;
  28. break;
  29. }
  30. }
  31. }
  32. public override JsValue Call(JsValue thisObject, JsValue[] jsArguments)
  33. {
  34. var parameterInfos = _d.Method.GetParameters();
  35. #if NETFRAMEWORK
  36. if (parameterInfos.Length > 0 && parameterInfos[0].ParameterType == typeof(System.Runtime.CompilerServices.Closure))
  37. {
  38. var reducedLength = parameterInfos.Length - 1;
  39. var reducedParameterInfos = new ParameterInfo[reducedLength];
  40. Array.Copy(parameterInfos, 1, reducedParameterInfos, 0, reducedLength);
  41. parameterInfos = reducedParameterInfos;
  42. }
  43. #endif
  44. int delegateArgumentsCount = parameterInfos.Length;
  45. int delegateNonParamsArgumentsCount = _delegateContainsParamsArgument ? delegateArgumentsCount - 1 : delegateArgumentsCount;
  46. int jsArgumentsCount = jsArguments.Length;
  47. int jsArgumentsWithoutParamsCount = Math.Min(jsArgumentsCount, delegateNonParamsArgumentsCount);
  48. var parameters = new object[delegateArgumentsCount];
  49. // convert non params parameter to expected types
  50. for (var i = 0; i < jsArgumentsWithoutParamsCount; i++)
  51. {
  52. var parameterType = parameterInfos[i].ParameterType;
  53. if (parameterType == typeof(JsValue))
  54. {
  55. parameters[i] = jsArguments[i];
  56. }
  57. else
  58. {
  59. parameters[i] = Engine.ClrTypeConverter.Convert(
  60. jsArguments[i].ToObject(),
  61. parameterType,
  62. CultureInfo.InvariantCulture);
  63. }
  64. }
  65. // assign null to parameters not provided
  66. for (var i = jsArgumentsWithoutParamsCount; i < delegateNonParamsArgumentsCount; i++)
  67. {
  68. if (parameterInfos[i].ParameterType.IsValueType)
  69. {
  70. parameters[i] = Activator.CreateInstance(parameterInfos[i].ParameterType);
  71. }
  72. else
  73. {
  74. parameters[i] = null;
  75. }
  76. }
  77. // assign params to array and converts each objet to expected type
  78. if (_delegateContainsParamsArgument)
  79. {
  80. int paramsArgumentIndex = delegateArgumentsCount - 1;
  81. int paramsCount = Math.Max(0, jsArgumentsCount - delegateNonParamsArgumentsCount);
  82. object[] paramsParameter = new object[paramsCount];
  83. var paramsParameterType = parameterInfos[paramsArgumentIndex].ParameterType.GetElementType();
  84. for (var i = paramsArgumentIndex; i < jsArgumentsCount; i++)
  85. {
  86. var paramsIndex = i - paramsArgumentIndex;
  87. if (paramsParameterType == typeof(JsValue))
  88. {
  89. paramsParameter[paramsIndex] = jsArguments[i];
  90. }
  91. else
  92. {
  93. paramsParameter[paramsIndex] = Engine.ClrTypeConverter.Convert(
  94. jsArguments[i].ToObject(),
  95. paramsParameterType,
  96. CultureInfo.InvariantCulture);
  97. }
  98. }
  99. parameters[paramsArgumentIndex] = paramsParameter;
  100. }
  101. try
  102. {
  103. return FromObject(Engine, _d.DynamicInvoke(parameters));
  104. }
  105. catch (TargetInvocationException exception)
  106. {
  107. ExceptionHelper.ThrowMeaningfulException(_engine, exception);
  108. throw;
  109. }
  110. }
  111. }
  112. }