|
@@ -3,30 +3,33 @@ using System.Collections.ObjectModel;
|
|
|
using System.Linq;
|
|
|
using System.Linq.Expressions;
|
|
|
using Jint.Native;
|
|
|
+using System.Collections.Generic;
|
|
|
|
|
|
namespace Jint.Runtime.Interop
|
|
|
{
|
|
|
public class DefaultTypeConverter : ITypeConverter
|
|
|
{
|
|
|
private readonly Engine _engine;
|
|
|
+ private static readonly Dictionary<string, bool> _knownConversions = new Dictionary<string, bool>();
|
|
|
+ private static readonly object _lockObject = new object();
|
|
|
|
|
|
public DefaultTypeConverter(Engine engine)
|
|
|
{
|
|
|
_engine = engine;
|
|
|
}
|
|
|
|
|
|
- public object Convert(object value, Type type, IFormatProvider formatProvider)
|
|
|
- {
|
|
|
- if (value == null)
|
|
|
- {
|
|
|
- if (TypeIsNullable(type))
|
|
|
- {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- throw new NotSupportedException(string.Format("Unable to convert null to '{0}'", type.FullName));
|
|
|
- }
|
|
|
-
|
|
|
+ public virtual object Convert(object value, Type type, IFormatProvider formatProvider)
|
|
|
+ {
|
|
|
+ if (value == null)
|
|
|
+ {
|
|
|
+ if (TypeConverter.TypeIsNullable(type))
|
|
|
+ {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ throw new NotSupportedException(string.Format("Unable to convert null to '{0}'", type.FullName));
|
|
|
+ }
|
|
|
+
|
|
|
// don't try to convert if value is derived from type
|
|
|
if (type.IsInstanceOfType(value))
|
|
|
{
|
|
@@ -35,7 +38,7 @@ namespace Jint.Runtime.Interop
|
|
|
|
|
|
if (type.IsEnum)
|
|
|
{
|
|
|
- var integer = System.Convert.ChangeType(value, typeof (int), formatProvider);
|
|
|
+ var integer = System.Convert.ChangeType(value, typeof(int), formatProvider);
|
|
|
if (integer == null)
|
|
|
{
|
|
|
throw new ArgumentOutOfRangeException();
|
|
@@ -46,9 +49,9 @@ namespace Jint.Runtime.Interop
|
|
|
|
|
|
var valueType = value.GetType();
|
|
|
// is the javascript value an ICallable instance ?
|
|
|
- if (valueType == typeof (Func<JsValue, JsValue[], JsValue>))
|
|
|
+ if (valueType == typeof(Func<JsValue, JsValue[], JsValue>))
|
|
|
{
|
|
|
- var function = (Func<JsValue, JsValue[], JsValue>) value;
|
|
|
+ var function = (Func<JsValue, JsValue[], JsValue>)value;
|
|
|
|
|
|
if (type.IsGenericType)
|
|
|
{
|
|
@@ -79,7 +82,7 @@ namespace Jint.Runtime.Interop
|
|
|
{
|
|
|
var genericArguments = type.GetGenericArguments();
|
|
|
var returnType = genericArguments.Last();
|
|
|
-
|
|
|
+
|
|
|
var @params = new ParameterExpression[genericArguments.Count() - 1];
|
|
|
for (var i = 0; i < @params.Count(); i++)
|
|
|
{
|
|
@@ -101,45 +104,74 @@ namespace Jint.Runtime.Interop
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- if (type == typeof (Action))
|
|
|
+ if (type == typeof(Action))
|
|
|
{
|
|
|
return (Action)(() => function(JsValue.Undefined, new JsValue[0]));
|
|
|
- }
|
|
|
- else if (type.IsSubclassOf(typeof(System.MulticastDelegate)))
|
|
|
- {
|
|
|
- var method = type.GetMethod("Invoke");
|
|
|
- var arguments = method.GetParameters();
|
|
|
-
|
|
|
- var @params = new ParameterExpression[arguments.Count()];
|
|
|
- for (var i = 0; i < @params.Count(); i++)
|
|
|
- {
|
|
|
- @params[i] = Expression.Parameter(typeof(object), arguments[i].Name);
|
|
|
- }
|
|
|
- var @vars = Expression.NewArrayInit(typeof(JsValue), @params.Select(p => Expression.Call(null, typeof(JsValue).GetMethod("FromObject"), Expression.Constant(_engine, typeof(Engine)), p)));
|
|
|
-
|
|
|
- var callExpression = Expression.Block(
|
|
|
- Expression.Call(
|
|
|
- Expression.Call(Expression.Constant(function.Target),
|
|
|
- function.Method,
|
|
|
- Expression.Constant(JsValue.Undefined, typeof(JsValue)),
|
|
|
- @vars),
|
|
|
- typeof(JsValue).GetMethod("ToObject")),
|
|
|
- Expression.Empty());
|
|
|
-
|
|
|
- var dynamicExpression = Expression.Invoke(Expression.Lambda(callExpression, new ReadOnlyCollection<ParameterExpression>(@params)), new ReadOnlyCollection<ParameterExpression>(@params));
|
|
|
-
|
|
|
- return Expression.Lambda(type, dynamicExpression, new ReadOnlyCollection<ParameterExpression>(@params));
|
|
|
+ }
|
|
|
+ else if (type.IsSubclassOf(typeof(System.MulticastDelegate)))
|
|
|
+ {
|
|
|
+ var method = type.GetMethod("Invoke");
|
|
|
+ var arguments = method.GetParameters();
|
|
|
+
|
|
|
+ var @params = new ParameterExpression[arguments.Count()];
|
|
|
+ for (var i = 0; i < @params.Count(); i++)
|
|
|
+ {
|
|
|
+ @params[i] = Expression.Parameter(typeof(object), arguments[i].Name);
|
|
|
+ }
|
|
|
+ var @vars = Expression.NewArrayInit(typeof(JsValue), @params.Select(p => Expression.Call(null, typeof(JsValue).GetMethod("FromObject"), Expression.Constant(_engine, typeof(Engine)), p)));
|
|
|
+
|
|
|
+ var callExpression = Expression.Block(
|
|
|
+ Expression.Call(
|
|
|
+ Expression.Call(Expression.Constant(function.Target),
|
|
|
+ function.Method,
|
|
|
+ Expression.Constant(JsValue.Undefined, typeof(JsValue)),
|
|
|
+ @vars),
|
|
|
+ typeof(JsValue).GetMethod("ToObject")),
|
|
|
+ Expression.Empty());
|
|
|
+
|
|
|
+ var dynamicExpression = Expression.Invoke(Expression.Lambda(callExpression, new ReadOnlyCollection<ParameterExpression>(@params)), new ReadOnlyCollection<ParameterExpression>(@params));
|
|
|
+
|
|
|
+ return Expression.Lambda(type, dynamicExpression, new ReadOnlyCollection<ParameterExpression>(@params));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return System.Convert.ChangeType(value, type, formatProvider);
|
|
|
- }
|
|
|
-
|
|
|
- private static bool TypeIsNullable(Type type)
|
|
|
- {
|
|
|
- return !type.IsValueType || Nullable.GetUnderlyingType(type) != null;
|
|
|
- }
|
|
|
+ }
|
|
|
+
|
|
|
+ public virtual bool TryConvert(object value, Type type, IFormatProvider formatProvider, out object converted)
|
|
|
+ {
|
|
|
+ bool canConvert;
|
|
|
+ var key = value == null ? String.Format("Null->{0}", type) : String.Format("{0}->{1}", value.GetType(), type);
|
|
|
+
|
|
|
+ lock (_lockObject)
|
|
|
+ {
|
|
|
+ if (!_knownConversions.TryGetValue(key, out canConvert))
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ converted = Convert(value, type, formatProvider);
|
|
|
+ _knownConversions.Add(key, true);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ catch
|
|
|
+ {
|
|
|
+ converted = null;
|
|
|
+ _knownConversions.Add(key, false);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (canConvert)
|
|
|
+ {
|
|
|
+ converted = Convert(value, type, formatProvider);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ converted = null;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
}
|
|
|
}
|