DefaultObjectConverter.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading;
  4. using Jint.Native;
  5. using Jint.Native.Array;
  6. using Jint.Runtime;
  7. using Jint.Runtime.Descriptors;
  8. using Jint.Runtime.Interop;
  9. namespace Jint
  10. {
  11. internal static class DefaultObjectConverter
  12. {
  13. private static Dictionary<Type, Func<Engine, object, JsValue>> _typeMappers = new()
  14. {
  15. { typeof(bool), (engine, v) => (bool)v ? JsBoolean.True : JsBoolean.False },
  16. { typeof(byte), (engine, v) => JsNumber.Create((byte)v) },
  17. { typeof(char), (engine, v) => JsString.Create((char)v) },
  18. { typeof(DateTime), (engine, v) => engine.Realm.Intrinsics.Date.Construct((DateTime)v) },
  19. { typeof(DateTimeOffset), (engine, v) => engine.Realm.Intrinsics.Date.Construct((DateTimeOffset)v) },
  20. { typeof(decimal), (engine, v) => (JsValue)(double)(decimal)v },
  21. { typeof(double), (engine, v) => (JsValue)(double)v },
  22. { typeof(short), (engine, v) => JsNumber.Create((short)v) },
  23. { typeof(int), (engine, v) => JsNumber.Create((int)v) },
  24. { typeof(long), (engine, v) => (JsValue)(long)v },
  25. { typeof(sbyte), (engine, v) => JsNumber.Create((sbyte)v) },
  26. { typeof(float), (engine, v) => (JsValue)(float)v },
  27. { typeof(string), (engine, v) => JsString.Create((string)v) },
  28. { typeof(ushort), (engine, v) => JsNumber.Create((ushort)v) },
  29. { typeof(uint), (engine, v) => JsNumber.Create((uint)v) },
  30. { typeof(ulong), (engine, v) => JsNumber.Create((ulong)v) },
  31. {
  32. typeof(System.Text.RegularExpressions.Regex),
  33. (engine, v) => engine.Realm.Intrinsics.RegExp.Construct((System.Text.RegularExpressions.Regex)v, "")
  34. }
  35. };
  36. public static bool TryConvert(Engine engine, object value, out JsValue result)
  37. {
  38. var valueType = value.GetType();
  39. var typeMappers = _typeMappers;
  40. if (typeMappers.TryGetValue(valueType, out var typeMapper))
  41. {
  42. result = typeMapper(engine, value);
  43. }
  44. else
  45. {
  46. if (value is Array a)
  47. {
  48. // racy, we don't care, worst case we'll catch up later
  49. Interlocked.CompareExchange(ref _typeMappers,
  50. new Dictionary<Type, Func<Engine, object, JsValue>>(typeMappers)
  51. {
  52. [valueType] = ConvertArray
  53. }, typeMappers);
  54. result = ConvertArray(engine, a);
  55. }
  56. else
  57. {
  58. if (value is Delegate d)
  59. {
  60. result = new DelegateWrapper(engine, d);
  61. }
  62. else
  63. {
  64. var t = value.GetType();
  65. if (t.IsEnum)
  66. {
  67. var ut = Enum.GetUnderlyingType(t);
  68. if (ut == typeof(ulong))
  69. {
  70. result = JsNumber.Create(Convert.ToDouble(value));
  71. }
  72. else
  73. {
  74. if (ut == typeof(uint) || ut == typeof(long))
  75. {
  76. result = JsNumber.Create(Convert.ToInt64(value));
  77. }
  78. else
  79. {
  80. result = JsNumber.Create(Convert.ToInt32(value));
  81. }
  82. }
  83. }
  84. else
  85. {
  86. result = engine.Options.Interop.WrapObjectHandler.Invoke(engine, value);
  87. }
  88. // if no known type could be guessed, use the default of wrapping using using ObjectWrapper.
  89. }
  90. }
  91. }
  92. return result is not null;
  93. }
  94. private static JsValue ConvertArray(Engine e, object v)
  95. {
  96. var array = (Array)v;
  97. var arrayLength = (uint)array.Length;
  98. var jsArray = new ArrayInstance(e, arrayLength)
  99. {
  100. _prototype = e.Realm.Intrinsics.Array.PrototypeObject
  101. };
  102. for (uint i = 0; i < arrayLength; ++i)
  103. {
  104. var jsItem = JsValue.FromObject(e, array.GetValue(i));
  105. jsArray.WriteArrayValue(i, new PropertyDescriptor(jsItem, PropertyFlag.ConfigurableEnumerableWritable));
  106. }
  107. jsArray.SetOwnProperty(CommonProperties.Length,
  108. new PropertyDescriptor(arrayLength, PropertyFlag.OnlyWritable));
  109. return jsArray;
  110. }
  111. }
  112. }