From 7fd79454ae0606b044cfc52ee53d374b6a913157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E9=91=AB=E4=BC=9F?= Date: Sat, 10 Apr 2021 15:32:56 +0800 Subject: [PATCH] Support converting js function into any delegate. --- Jint/Runtime/Interop/DefaultTypeConverter.cs | 142 ++++++------------- 1 file changed, 42 insertions(+), 100 deletions(-) diff --git a/Jint/Runtime/Interop/DefaultTypeConverter.cs b/Jint/Runtime/Interop/DefaultTypeConverter.cs index eafe23116b..3c308182c3 100644 --- a/Jint/Runtime/Interop/DefaultTypeConverter.cs +++ b/Jint/Runtime/Interop/DefaultTypeConverter.cs @@ -78,122 +78,64 @@ public virtual object Convert(object value, Type type, IFormatProvider formatPro { var function = (Func)value; - if (type.IsGenericType) + if (typeof(Delegate).IsAssignableFrom(type) && !type.IsAbstract) { - var genericType = type.GetGenericTypeDefinition(); + var method = type.GetMethod("Invoke"); + var arguments = method.GetParameters(); - // create the requested Delegate - if (genericType.Name.StartsWith("Action")) + var @params = new ParameterExpression[arguments.Length]; + for (var i = 0; i < @params.Length; i++) { - var genericArguments = type.GetGenericArguments(); + @params[i] = Expression.Parameter(arguments[i].ParameterType, arguments[i].Name); + } - var @params = new ParameterExpression[genericArguments.Length]; - for (var i = 0; i < @params.Length; i++) + var initializers = new MethodCallExpression[@params.Length]; + for (int i = 0; i < @params.Length; i++) + { + var param = @params[i]; + if (param.Type.IsValueType) { - @params[i] = Expression.Parameter(genericArguments[i], genericArguments[i].Name + i); + var boxing = Expression.Convert(param, objectType); + initializers[i] = Expression.Call(null, jsValueFromObject, Expression.Constant(_engine, engineType), boxing); } - var tmpVars = new Expression[@params.Length]; - for (var i = 0; i < @params.Length; i++) + else { - var param = @params[i]; - if (param.Type.IsValueType) - { - var boxing = Expression.Convert(param, objectType); - tmpVars[i] = Expression.Call(null, jsValueFromObject, Expression.Constant(_engine, engineType), boxing); - } - else - { - tmpVars[i] = Expression.Call(null, jsValueFromObject, Expression.Constant(_engine, engineType), param); - } + initializers[i] = Expression.Call(null, jsValueFromObject, Expression.Constant(_engine, engineType), param); } - var @vars = Expression.NewArrayInit(jsValueType, tmpVars); - - var callExpresion = Expression.Block(Expression.Call( - Expression.Call(Expression.Constant(function.Target), - function.Method, - Expression.Constant(JsValue.Undefined, jsValueType), - @vars), - jsValueToObject), Expression.Empty()); - - return Expression.Lambda(callExpresion, new ReadOnlyCollection(@params)).Compile(); } - else if (genericType.Name.StartsWith("Func")) - { - var genericArguments = type.GetGenericArguments(); - var returnType = genericArguments[genericArguments.Length - 1]; - var @params = new ParameterExpression[genericArguments.Length - 1]; - for (var i = 0; i < @params.Length; i++) - { - @params[i] = Expression.Parameter(genericArguments[i], genericArguments[i].Name + i); - } + var @vars = Expression.NewArrayInit(jsValueType, initializers); - var initializers = new MethodCallExpression[@params.Length]; - for (int i = 0; i < @params.Length; i++) - { - var boxingExpression = Expression.Convert(@params[i], objectType); - initializers[i]= Expression.Call(null, jsValueFromObject, Expression.Constant(_engine, engineType), boxingExpression); - } - var @vars = Expression.NewArrayInit(jsValueType, initializers); - - // the final result's type needs to be changed before casting, - // for instance when a function returns a number (double) but C# expects an integer - - var callExpresion = Expression.Convert( - Expression.Call(null, - convertChangeType, - Expression.Call( - Expression.Call(Expression.Constant(function.Target), - function.Method, - Expression.Constant(JsValue.Undefined, jsValueType), - @vars), - jsValueToObject), - Expression.Constant(returnType, typeType), - Expression.Constant(System.Globalization.CultureInfo.InvariantCulture, typeof(IFormatProvider)) - ), - returnType); - - return Expression.Lambda(callExpresion, new ReadOnlyCollection(@params)).Compile(); - } - } - else - { - if (type == typeof(Action)) + var callExpression = Expression.Call( + Expression.Constant(function.Target), + function.Method, + Expression.Constant(JsValue.Undefined, jsValueType), + @vars); + + if (method.ReturnType != typeof(void)) { - return (Action)(() => function(JsValue.Undefined, System.Array.Empty())); + return Expression.Lambda( + type, + Expression.Convert( + Expression.Call( + null, + convertChangeType, + Expression.Call(callExpression, jsValueToObject), + Expression.Constant(method.ReturnType), + Expression.Constant(System.Globalization.CultureInfo.InvariantCulture, typeof(IFormatProvider)) + ), + method.ReturnType + ), + new ReadOnlyCollection(@params)).Compile(); } - else if (typeof(MulticastDelegate).IsAssignableFrom(type)) + else { - var method = type.GetMethod("Invoke"); - var arguments = method.GetParameters(); - - var @params = new ParameterExpression[arguments.Length]; - for (var i = 0; i < @params.Length; i++) - { - @params[i] = Expression.Parameter(objectType, arguments[i].Name); - } - - var initializers = new MethodCallExpression[@params.Length]; - for (int i = 0; i < @params.Length; i++) - { - initializers[i] = Expression.Call(null, jsValueType.GetMethod("FromObject"), Expression.Constant(_engine, engineType), @params[i]); - } - - var @vars = Expression.NewArrayInit(jsValueType, initializers); - - var callExpression = Expression.Call( - Expression.Call(Expression.Constant(function.Target), - function.Method, - Expression.Constant(JsValue.Undefined, jsValueType), - @vars), - jsValueType.GetMethod("ToObject")); - - var dynamicExpression = Expression.Invoke(Expression.Lambda(callExpression, new ReadOnlyCollection(@params)), new ReadOnlyCollection(@params)); - - return Expression.Lambda(type, dynamicExpression, new ReadOnlyCollection(@params)).Compile(); + return Expression.Lambda( + type, + callExpression, + new ReadOnlyCollection(@params)).Compile(); } } - } if (type.IsArray)