ReflectionAccessor.cs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. using System.Globalization;
  2. using System.Reflection;
  3. using Jint.Extensions;
  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. public Type MemberType => _memberType;
  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. ExceptionHelper.ThrowMeaningfulException(engine, tie);
  48. }
  49. }
  50. return value;
  51. }
  52. protected virtual JsValue? ConstantValue => null;
  53. private object? TryReadFromIndexer(object target)
  54. {
  55. var getter = _indexer?.GetGetMethod();
  56. if (getter is null)
  57. {
  58. return null;
  59. }
  60. try
  61. {
  62. object[] parameters = { _memberName! };
  63. return getter.Invoke(target, parameters);
  64. }
  65. catch
  66. {
  67. return null;
  68. }
  69. }
  70. public void SetValue(Engine engine, object target, JsValue value)
  71. {
  72. object? converted;
  73. if (_memberType == typeof(JsValue))
  74. {
  75. converted = value;
  76. }
  77. else if (!ReflectionExtensions.TryConvertViaTypeCoercion(_memberType, engine.Options.Interop.ValueCoercion, value, out converted))
  78. {
  79. // attempt to convert the JsValue to the target type
  80. converted = value.ToObject();
  81. if (converted != null && converted.GetType() != _memberType)
  82. {
  83. converted = ConvertValueToSet(engine, converted);
  84. }
  85. }
  86. try
  87. {
  88. DoSetValue(target, converted);
  89. }
  90. catch (TargetInvocationException exception)
  91. {
  92. ExceptionHelper.ThrowMeaningfulException(engine, exception);
  93. }
  94. }
  95. protected virtual object? ConvertValueToSet(Engine engine, object value)
  96. {
  97. return engine.ClrTypeConverter.Convert(value, _memberType, CultureInfo.InvariantCulture);
  98. }
  99. public virtual PropertyDescriptor CreatePropertyDescriptor(Engine engine, object target, bool enumerable = true)
  100. {
  101. return new ReflectionDescriptor(engine, this, target, enumerable);
  102. }
  103. }
  104. }