ReflectionAccessor.cs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. using System.Diagnostics.CodeAnalysis;
  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. #pragma warning disable IL2098
  9. #pragma warning disable IL2072
  10. #pragma warning disable IL2077
  11. namespace Jint.Runtime.Interop.Reflection;
  12. /// <summary>
  13. /// Strategy to read and write CLR object properties and fields.
  14. /// </summary>
  15. internal abstract class ReflectionAccessor
  16. {
  17. private readonly Type? _memberType;
  18. private readonly PropertyInfo? _indexer;
  19. public Type? MemberType => _memberType;
  20. protected ReflectionAccessor(
  21. Type? memberType,
  22. PropertyInfo? indexer = null)
  23. {
  24. _memberType = memberType;
  25. _indexer = indexer;
  26. }
  27. public virtual bool Readable => true;
  28. public abstract bool Writable { get; }
  29. protected abstract object? DoGetValue(object target, string memberName);
  30. protected abstract void DoSetValue(object target, string memberName, object? value);
  31. public object? GetValue(Engine engine, object target, string memberName)
  32. {
  33. var constantValue = ConstantValue;
  34. if (constantValue is not null)
  35. {
  36. return constantValue;
  37. }
  38. // first check indexer so we don't confuse inherited properties etc
  39. var value = TryReadFromIndexer(target, memberName);
  40. if (value is null)
  41. {
  42. try
  43. {
  44. value = DoGetValue(target, memberName);
  45. }
  46. catch (TargetInvocationException tie)
  47. {
  48. Throw.MeaningfulException(engine, tie);
  49. }
  50. }
  51. return value;
  52. }
  53. protected virtual JsValue? ConstantValue => null;
  54. private object? TryReadFromIndexer(object target, string memberName)
  55. {
  56. var getter = _indexer?.GetGetMethod();
  57. if (getter is null)
  58. {
  59. return null;
  60. }
  61. try
  62. {
  63. object[] parameters = [memberName];
  64. return getter.Invoke(target, parameters);
  65. }
  66. catch
  67. {
  68. return null;
  69. }
  70. }
  71. public void SetValue(Engine engine, object target, string memberName, JsValue value)
  72. {
  73. object? converted;
  74. if (_memberType == typeof(JsValue))
  75. {
  76. converted = value;
  77. }
  78. else if (!ReflectionExtensions.TryConvertViaTypeCoercion(_memberType, engine.Options.Interop.ValueCoercion, value, out converted))
  79. {
  80. // attempt to convert the JsValue to the target type
  81. converted = value.ToObject();
  82. if (converted != null && converted.GetType() != _memberType)
  83. {
  84. converted = ConvertValueToSet(engine, converted);
  85. }
  86. }
  87. try
  88. {
  89. DoSetValue(target, memberName, converted);
  90. }
  91. catch (TargetInvocationException exception)
  92. {
  93. Throw.MeaningfulException(engine, exception);
  94. }
  95. }
  96. protected virtual object? ConvertValueToSet(Engine engine, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)] object value)
  97. {
  98. var memberType = _memberType ?? value.GetType();
  99. return engine.TypeConverter.Convert(value, memberType, CultureInfo.InvariantCulture);
  100. }
  101. public virtual PropertyDescriptor CreatePropertyDescriptor(Engine engine, object target, string memberName, bool enumerable = true)
  102. {
  103. return new ReflectionDescriptor(engine, this, target, memberName, enumerable);
  104. }
  105. }