IndexerAccessor.cs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Globalization;
  5. using System.Reflection;
  6. using Jint.Native;
  7. namespace Jint.Runtime.Interop.Reflection
  8. {
  9. internal sealed class IndexerAccessor : ReflectionAccessor
  10. {
  11. private readonly object _key;
  12. private readonly PropertyInfo _indexer;
  13. private readonly MethodInfo _getter;
  14. private readonly MethodInfo _setter;
  15. private readonly MethodInfo _containsKey;
  16. private static readonly PropertyInfo _iListIndexer = typeof(IList).GetProperty("Item");
  17. private IndexerAccessor(PropertyInfo indexer, MethodInfo containsKey, object key)
  18. : base(indexer.PropertyType, key)
  19. {
  20. _indexer = indexer;
  21. _containsKey = containsKey;
  22. _key = key;
  23. _getter = indexer.GetGetMethod();
  24. _setter = indexer.GetSetMethod();
  25. }
  26. internal static bool TryFindIndexer(
  27. Engine engine,
  28. Type targetType,
  29. string propertyName,
  30. out IndexerAccessor indexerAccessor,
  31. out PropertyInfo indexer)
  32. {
  33. var paramTypeArray = new Type[1];
  34. IndexerAccessor ComposeIndexerFactory(PropertyInfo candidate, Type paramType)
  35. {
  36. if (engine.ClrTypeConverter.TryConvert(propertyName, paramType, CultureInfo.InvariantCulture, out var key))
  37. {
  38. // the key can be converted for this indexer
  39. var indexerProperty = candidate;
  40. // get contains key method to avoid index exception being thrown in dictionaries
  41. paramTypeArray[0] = paramType;
  42. var containsKeyMethod = targetType.GetMethod(nameof(IDictionary<string, string>.ContainsKey), paramTypeArray);
  43. if (containsKeyMethod is null)
  44. {
  45. paramTypeArray[0] = typeof(object);
  46. containsKeyMethod = targetType.GetMethod(nameof(IDictionary.Contains), paramTypeArray);
  47. }
  48. return new IndexerAccessor(indexerProperty, containsKeyMethod, key);
  49. }
  50. // the key type doesn't work for this indexer
  51. return null;
  52. }
  53. // default indexer wins
  54. if (typeof(IList).IsAssignableFrom(targetType))
  55. {
  56. indexerAccessor = ComposeIndexerFactory(_iListIndexer, typeof(int));
  57. if (indexerAccessor != null)
  58. {
  59. indexer = _iListIndexer;
  60. return true;
  61. }
  62. }
  63. // try to find first indexer having either public getter or setter with matching argument type
  64. foreach (var candidate in targetType.GetProperties())
  65. {
  66. var indexParameters = candidate.GetIndexParameters();
  67. if (indexParameters.Length != 1)
  68. {
  69. continue;
  70. }
  71. if (candidate.GetGetMethod() != null || candidate.GetSetMethod() != null)
  72. {
  73. var paramType = indexParameters[0].ParameterType;
  74. indexerAccessor = ComposeIndexerFactory(candidate, paramType);
  75. if (indexerAccessor != null)
  76. {
  77. indexer = candidate;
  78. return true;
  79. }
  80. }
  81. }
  82. indexerAccessor = default;
  83. indexer = default;
  84. return false;
  85. }
  86. public override bool Writable => _indexer.CanWrite;
  87. protected override object DoGetValue(object target)
  88. {
  89. if (_getter is null)
  90. {
  91. ExceptionHelper.ThrowInvalidOperationException("Indexer has no public getter.");
  92. return null;
  93. }
  94. object[] parameters = {_key};
  95. if (_containsKey != null)
  96. {
  97. if (_containsKey.Invoke(target, parameters) as bool? != true)
  98. {
  99. return JsValue.Undefined;
  100. }
  101. }
  102. return _getter.Invoke(target, parameters);
  103. }
  104. protected override void DoSetValue(object target, object value)
  105. {
  106. if (_setter is null)
  107. {
  108. ExceptionHelper.ThrowInvalidOperationException("Indexer has no public setter.");
  109. }
  110. object[] parameters = {_key, value};
  111. _setter!.Invoke(target, parameters);
  112. }
  113. }
  114. }