DelegateWrapper.cs 4.8 KB

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