IndexDescriptor.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. using System;
  2. using System.Globalization;
  3. using System.Reflection;
  4. using Jint.Native;
  5. namespace Jint.Runtime.Descriptors.Specialized
  6. {
  7. public sealed class IndexDescriptor : PropertyDescriptor
  8. {
  9. private readonly Engine _engine;
  10. private readonly object _key;
  11. private readonly object _target;
  12. private readonly PropertyInfo _indexer;
  13. private readonly MethodInfo _containsKey;
  14. public IndexDescriptor(Engine engine, Type targetType, string key, object target)
  15. : base(PropertyFlag.Enumerable | PropertyFlag.CustomJsValue)
  16. {
  17. _engine = engine;
  18. _target = target;
  19. if (!TryFindIndexer(engine, targetType, key, out _indexer, out _containsKey, out _key))
  20. {
  21. ExceptionHelper.ThrowArgumentException("invalid indexing configuration, target indexer not found");
  22. }
  23. Writable = engine.Options._IsClrWriteAllowed;
  24. }
  25. public IndexDescriptor(Engine engine, string key, object item)
  26. : this(engine, item.GetType(), key, item)
  27. {
  28. }
  29. internal static bool TryFindIndexer(
  30. Engine engine,
  31. Type targetType,
  32. string propertyName,
  33. out PropertyInfo indexerProperty,
  34. out MethodInfo containsKeyMethod,
  35. out object indexerKey)
  36. {
  37. // get all instance indexers with exactly 1 argument
  38. var paramTypeArray = new Type[1];
  39. // try to find first indexer having either public getter or setter with matching argument type
  40. foreach (var candidate in targetType.GetProperties())
  41. {
  42. var indexParameters = candidate.GetIndexParameters();
  43. if (indexParameters.Length != 1)
  44. {
  45. continue;
  46. }
  47. if (candidate.GetGetMethod() != null || candidate.GetSetMethod() != null)
  48. {
  49. var paramType = indexParameters[0].ParameterType;
  50. if (engine.ClrTypeConverter.TryConvert(propertyName, paramType, CultureInfo.InvariantCulture, out indexerKey))
  51. {
  52. indexerProperty = candidate;
  53. // get contains key method to avoid index exception being thrown in dictionaries
  54. paramTypeArray[0] = paramType;
  55. containsKeyMethod = targetType.GetMethod("ContainsKey", paramTypeArray);
  56. return true;
  57. }
  58. }
  59. }
  60. indexerProperty = default;
  61. containsKeyMethod = default;
  62. indexerKey = default;
  63. return false;
  64. }
  65. protected internal override JsValue CustomValue
  66. {
  67. get
  68. {
  69. var getter = _indexer.GetGetMethod();
  70. if (getter == null)
  71. {
  72. ExceptionHelper.ThrowInvalidOperationException("Indexer has no public getter.");
  73. }
  74. object[] parameters = { _key };
  75. if (_containsKey != null)
  76. {
  77. if ((_containsKey.Invoke(_target, parameters) as bool?) != true)
  78. {
  79. return JsValue.Undefined;
  80. }
  81. }
  82. try
  83. {
  84. return JsValue.FromObject(_engine, getter.Invoke(_target, parameters));
  85. }
  86. catch (TargetInvocationException tie)
  87. {
  88. switch (tie.InnerException)
  89. {
  90. case null:
  91. throw;
  92. case ArgumentOutOfRangeException _:
  93. return JsValue.Undefined;
  94. case IndexOutOfRangeException _:
  95. return JsValue.Undefined;
  96. default:
  97. throw tie.InnerException;
  98. }
  99. }
  100. }
  101. set
  102. {
  103. var setter = _indexer.GetSetMethod();
  104. if (setter == null)
  105. {
  106. ExceptionHelper.ThrowInvalidOperationException("Indexer has no public setter.");
  107. }
  108. var obj = value?.ToObject();
  109. // attempt to convert to expected type
  110. if (obj != null && obj.GetType() != _indexer.PropertyType)
  111. {
  112. obj = _engine.ClrTypeConverter.Convert(obj, _indexer.PropertyType, CultureInfo.InvariantCulture);
  113. }
  114. object[] parameters = { _key, obj };
  115. try
  116. {
  117. setter!.Invoke(_target, parameters);
  118. }
  119. catch (TargetInvocationException tie)
  120. {
  121. if (tie.InnerException != null)
  122. {
  123. throw tie.InnerException;
  124. }
  125. throw;
  126. }
  127. }
  128. }
  129. }
  130. }