123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- using System.Globalization;
- using System.Reflection;
- using Jint.Extensions;
- using Jint.Native;
- using Jint.Native.Function;
- #pragma warning disable IL2072
- #pragma warning disable IL3050
- namespace Jint.Runtime.Interop;
- /// <summary>
- /// Represents a FunctionInstance wrapper around a CLR method. This is used by user to pass
- /// custom methods to the engine.
- /// </summary>
- internal sealed class DelegateWrapper : Function
- {
- private static readonly JsString _name = new JsString("delegate");
- private readonly Delegate _d;
- private readonly bool _delegateContainsParamsArgument;
- public DelegateWrapper(
- Engine engine, Delegate d)
- : base(engine, engine.Realm, _name, FunctionThisMode.Global)
- {
- _d = d;
- _prototype = engine.Realm.Intrinsics.Function.PrototypeObject;
- var parameterInfos = _d.Method.GetParameters();
- _delegateContainsParamsArgument = false;
- foreach (var p in parameterInfos)
- {
- if (Attribute.IsDefined(p, typeof(ParamArrayAttribute)))
- {
- _delegateContainsParamsArgument = true;
- break;
- }
- }
- }
- protected internal override JsValue Call(JsValue thisObject, JsCallArguments arguments)
- {
- var parameterInfos = _d.Method.GetParameters();
- #if NETFRAMEWORK
- if (parameterInfos.Length > 0 && parameterInfos[0].ParameterType == typeof(System.Runtime.CompilerServices.Closure))
- {
- var reducedLength = parameterInfos.Length - 1;
- var reducedParameterInfos = new ParameterInfo[reducedLength];
- Array.Copy(parameterInfos, 1, reducedParameterInfos, 0, reducedLength);
- parameterInfos = reducedParameterInfos;
- }
- #endif
- int delegateArgumentsCount = parameterInfos.Length;
- int delegateNonParamsArgumentsCount = _delegateContainsParamsArgument ? delegateArgumentsCount - 1 : delegateArgumentsCount;
- int jsArgumentsCount = arguments.Length;
- int jsArgumentsWithoutParamsCount = Math.Min(jsArgumentsCount, delegateNonParamsArgumentsCount);
- var clrTypeConverter = Engine.TypeConverter;
- var valueCoercionType = Engine.Options.Interop.ValueCoercion;
- var parameters = new object?[delegateArgumentsCount];
- // convert non params parameter to expected types
- for (var i = 0; i < jsArgumentsWithoutParamsCount; i++)
- {
- var parameterType = parameterInfos[i].ParameterType;
- var value = arguments[i];
- object? converted;
- if (typeof(JsValue).IsAssignableFrom(parameterType))
- {
- converted = value;
- }
- else if (!ReflectionExtensions.TryConvertViaTypeCoercion(parameterType, valueCoercionType, value, out converted))
- {
- converted = clrTypeConverter.Convert(
- value.ToObject(),
- parameterType,
- CultureInfo.InvariantCulture);
- }
- parameters[i] = converted;
- }
- // assign null to parameters not provided
- for (var i = jsArgumentsWithoutParamsCount; i < delegateNonParamsArgumentsCount; i++)
- {
- if (parameterInfos[i].ParameterType.IsValueType)
- {
- parameters[i] = Activator.CreateInstance(parameterInfos[i].ParameterType);
- }
- else
- {
- parameters[i] = null;
- }
- }
- // assign params to array and converts each object to expected type
- if (_delegateContainsParamsArgument)
- {
- int paramsArgumentIndex = delegateArgumentsCount - 1;
- int paramsCount = Math.Max(0, jsArgumentsCount - delegateNonParamsArgumentsCount);
- var paramsParameterType = parameterInfos[paramsArgumentIndex].ParameterType.GetElementType();
- var paramsParameter = Array.CreateInstance(paramsParameterType!, paramsCount);
- for (var i = paramsArgumentIndex; i < jsArgumentsCount; i++)
- {
- var paramsIndex = i - paramsArgumentIndex;
- var value = arguments[i];
- object? converted;
- if (paramsParameterType == typeof(JsValue))
- {
- converted = value;
- }
- else if (!ReflectionExtensions.TryConvertViaTypeCoercion(paramsParameterType, valueCoercionType, value, out converted))
- {
- converted = Engine.TypeConverter.Convert(
- value.ToObject(),
- paramsParameterType!,
- CultureInfo.InvariantCulture);
- }
- paramsParameter.SetValue(converted, paramsIndex);
- }
- parameters[paramsArgumentIndex] = paramsParameter;
- }
- try
- {
- var result = _d.DynamicInvoke(parameters);
- if (!IsAwaitable(result))
- {
- return FromObject(Engine, result);
- }
- return ConvertAwaitableToPromise(Engine, result!);
- }
- catch (TargetInvocationException exception)
- {
- Throw.MeaningfulException(Engine, exception);
- throw;
- }
- }
- private static bool IsAwaitable(object? obj)
- {
- if (obj is null)
- {
- return false;
- }
- if (obj is Task)
- {
- return true;
- }
- #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP
- if (obj is ValueTask)
- {
- return true;
- }
- // ValueTask<T> is not derived from ValueTask, so we need to check for it explicitly
- var type = obj.GetType();
- if (!type.IsGenericType)
- {
- return false;
- }
- return type.GetGenericTypeDefinition() == typeof(ValueTask<>);
- #else
- return false;
- #endif
- }
- }
|