ReflectionAccessor.cs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. using System;
  2. using System.Globalization;
  3. using System.Reflection;
  4. using Jint.Extensions;
  5. using Jint.Native;
  6. using Jint.Runtime.Descriptors;
  7. using Jint.Runtime.Descriptors.Specialized;
  8. namespace Jint.Runtime.Interop.Reflection
  9. {
  10. /// <summary>
  11. /// Strategy to read and write CLR object properties and fields.
  12. /// </summary>
  13. internal abstract class ReflectionAccessor
  14. {
  15. private readonly Type _memberType;
  16. private readonly object _memberName;
  17. private readonly PropertyInfo _indexer;
  18. protected ReflectionAccessor(
  19. Type memberType,
  20. object memberName,
  21. PropertyInfo indexer = null)
  22. {
  23. _memberType = memberType;
  24. _memberName = memberName;
  25. _indexer = indexer;
  26. }
  27. public abstract bool Writable { get; }
  28. protected abstract object DoGetValue(object target);
  29. protected abstract void DoSetValue(object target, object value);
  30. public object GetValue(Engine engine, object target)
  31. {
  32. var constantValue = ConstantValue;
  33. if (constantValue is not null)
  34. {
  35. return constantValue;
  36. }
  37. // first check indexer so we don't confuse inherited properties etc
  38. var value = TryReadFromIndexer(target);
  39. if (value is null)
  40. {
  41. try
  42. {
  43. value = DoGetValue(target);
  44. }
  45. catch (TargetInvocationException tie)
  46. {
  47. switch (tie.InnerException)
  48. {
  49. case ArgumentOutOfRangeException _:
  50. case IndexOutOfRangeException _:
  51. case InvalidOperationException _:
  52. case NotSupportedException _:
  53. return JsValue.Undefined;
  54. }
  55. ExceptionHelper.ThrowMeaningfulException(engine, tie);
  56. }
  57. }
  58. return value;
  59. }
  60. protected virtual JsValue ConstantValue => null;
  61. private object TryReadFromIndexer(object target)
  62. {
  63. var getter = _indexer?.GetGetMethod();
  64. if (getter is null)
  65. {
  66. return null;
  67. }
  68. try
  69. {
  70. object[] parameters = { _memberName };
  71. return getter.Invoke(target, parameters);
  72. }
  73. catch
  74. {
  75. return null;
  76. }
  77. }
  78. public void SetValue(Engine engine, object target, JsValue value)
  79. {
  80. object converted = null;
  81. if (_memberType == typeof(JsValue))
  82. {
  83. converted = value;
  84. }
  85. else if (!ReflectionExtensions.TryConvertViaTypeCoercion(_memberType, engine.Options.Interop.ValueCoercion, value, out converted))
  86. {
  87. // attempt to convert the JsValue to the target type
  88. converted = value.ToObject();
  89. if (converted != null && converted.GetType() != _memberType)
  90. {
  91. converted = ConvertValueToSet(engine, converted);
  92. }
  93. }
  94. try
  95. {
  96. DoSetValue(target, converted);
  97. }
  98. catch (TargetInvocationException exception)
  99. {
  100. ExceptionHelper.ThrowMeaningfulException(engine, exception);
  101. }
  102. }
  103. protected virtual object ConvertValueToSet(Engine engine, object value)
  104. {
  105. return engine.ClrTypeConverter.Convert(value, _memberType, CultureInfo.InvariantCulture);
  106. }
  107. public virtual PropertyDescriptor CreatePropertyDescriptor(Engine engine, object target)
  108. {
  109. return new ReflectionDescriptor(engine, this, target);
  110. }
  111. }
  112. }