DefaultTypeConverter.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. using System;
  2. using System.Collections.ObjectModel;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using Jint.Native;
  6. using System.Collections.Generic;
  7. namespace Jint.Runtime.Interop
  8. {
  9. public class DefaultTypeConverter : ITypeConverter
  10. {
  11. private readonly Engine _engine;
  12. private static readonly Dictionary<string, bool> _knownConversions = new Dictionary<string, bool>();
  13. private static readonly object _lockObject = new object();
  14. public DefaultTypeConverter(Engine engine)
  15. {
  16. _engine = engine;
  17. }
  18. public virtual object Convert(object value, Type type, IFormatProvider formatProvider)
  19. {
  20. // don't try to convert if value is derived from type
  21. if (type.IsInstanceOfType(value))
  22. {
  23. return value;
  24. }
  25. if (type.IsEnum)
  26. {
  27. var integer = System.Convert.ChangeType(value, typeof(int), formatProvider);
  28. if (integer == null)
  29. {
  30. throw new ArgumentOutOfRangeException();
  31. }
  32. return Enum.ToObject(type, integer);
  33. }
  34. var valueType = value.GetType();
  35. // is the javascript value an ICallable instance ?
  36. if (valueType == typeof (Func<JsValue, JsValue[], JsValue>))
  37. {
  38. var function = (Func<JsValue, JsValue[], JsValue>) value;
  39. if (type.IsGenericType)
  40. {
  41. var genericType = type.GetGenericTypeDefinition();
  42. // create the requested Delegate
  43. if (genericType.Name.StartsWith("Action"))
  44. {
  45. var genericArguments = type.GetGenericArguments();
  46. var @params = new ParameterExpression[genericArguments.Count()];
  47. for (var i = 0; i < @params.Count(); i++)
  48. {
  49. @params[i] = Expression.Parameter(genericArguments[i], genericArguments[i].Name + i);
  50. }
  51. var @vars = Expression.NewArrayInit(typeof(JsValue), @params.Select(p => Expression.Call(null, typeof(JsValue).GetMethod("FromObject"), Expression.Constant(_engine, typeof(Engine)), p)));
  52. var callExpresion = Expression.Block(Expression.Call(
  53. Expression.Call(Expression.Constant(function.Target),
  54. function.Method,
  55. Expression.Constant(JsValue.Undefined, typeof(JsValue)),
  56. @vars),
  57. typeof(JsValue).GetMethod("ToObject")), Expression.Empty());
  58. return Expression.Lambda(callExpresion, new ReadOnlyCollection<ParameterExpression>(@params));
  59. }
  60. else if (genericType.Name.StartsWith("Func"))
  61. {
  62. var genericArguments = type.GetGenericArguments();
  63. var returnType = genericArguments.Last();
  64. var @params = new ParameterExpression[genericArguments.Count() - 1];
  65. for (var i = 0; i < @params.Count(); i++)
  66. {
  67. @params[i] = Expression.Parameter(genericArguments[i], genericArguments[i].Name + i);
  68. }
  69. var @vars = Expression.NewArrayInit(typeof(JsValue), @params.Select(p => Expression.Call(null, typeof(JsValue).GetMethod("FromObject"), Expression.Constant(_engine, typeof(Engine)), p)));
  70. var callExpresion = Expression.Convert(
  71. Expression.Call(
  72. Expression.Call(Expression.Constant(function.Target),
  73. function.Method,
  74. Expression.Constant(JsValue.Undefined, typeof(JsValue)),
  75. @vars),
  76. typeof(JsValue).GetMethod("ToObject")),
  77. returnType);
  78. return Expression.Lambda(callExpresion, new ReadOnlyCollection<ParameterExpression>(@params));
  79. }
  80. }
  81. else
  82. {
  83. if (type == typeof (Action))
  84. {
  85. return (Action)(() => function(JsValue.Undefined, new JsValue[0]));
  86. }
  87. else if (type.IsSubclassOf(typeof(System.MulticastDelegate)))
  88. {
  89. var method = type.GetMethod("Invoke");
  90. var arguments = method.GetParameters();
  91. var @params = new ParameterExpression[arguments.Count()];
  92. for (var i = 0; i < @params.Count(); i++)
  93. {
  94. @params[i] = Expression.Parameter(typeof(object), arguments[i].Name);
  95. }
  96. var @vars = Expression.NewArrayInit(typeof(JsValue), @params.Select(p => Expression.Call(null, typeof(JsValue).GetMethod("FromObject"), Expression.Constant(_engine, typeof(Engine)), p)));
  97. var callExpression = Expression.Block(
  98. Expression.Call(
  99. Expression.Call(Expression.Constant(function.Target),
  100. function.Method,
  101. Expression.Constant(JsValue.Undefined, typeof(JsValue)),
  102. @vars),
  103. typeof(JsValue).GetMethod("ToObject")),
  104. Expression.Empty());
  105. var dynamicExpression = Expression.Invoke(Expression.Lambda(callExpression, new ReadOnlyCollection<ParameterExpression>(@params)), new ReadOnlyCollection<ParameterExpression>(@params));
  106. return Expression.Lambda(type, dynamicExpression, new ReadOnlyCollection<ParameterExpression>(@params));
  107. }
  108. }
  109. }
  110. return System.Convert.ChangeType(value, type, formatProvider);
  111. }
  112. public virtual bool TryConvert(object value, Type type, IFormatProvider formatProvider, out object converted)
  113. {
  114. bool canConvert;
  115. var key = value == null ? String.Format("Null->{0}", type) : String.Format("{0}->{1}", value.GetType(), type);
  116. if (!_knownConversions.TryGetValue(key, out canConvert))
  117. {
  118. lock (_lockObject)
  119. {
  120. if (!_knownConversions.TryGetValue(key, out canConvert))
  121. {
  122. try
  123. {
  124. converted = Convert(value, type, formatProvider);
  125. _knownConversions.Add(key, true);
  126. return true;
  127. }
  128. catch
  129. {
  130. converted = null;
  131. _knownConversions.Add(key, false);
  132. return false;
  133. }
  134. }
  135. }
  136. }
  137. if (canConvert)
  138. {
  139. converted = Convert(value, type, formatProvider);
  140. return true;
  141. }
  142. converted = null;
  143. return false;
  144. }
  145. }
  146. }