ReflectionAccessor.cs 3.7 KB

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