IndexDescriptor.cs 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  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 _item;
  12. private readonly PropertyInfo _indexer;
  13. private readonly MethodInfo _containsKey;
  14. public IndexDescriptor(Engine engine, Type targetType, string key, object item) : base(PropertyFlag.CustomJsValue)
  15. {
  16. _engine = engine;
  17. _item = item;
  18. // get all instance indexers with exactly 1 argument
  19. var indexers = targetType.GetProperties();
  20. var paramTypeArray = new Type[1];
  21. // try to find first indexer having either public getter or setter with matching argument type
  22. foreach (var indexer in indexers)
  23. {
  24. var indexParameters = indexer.GetIndexParameters();
  25. if (indexParameters.Length != 1)
  26. {
  27. continue;
  28. }
  29. if (indexer.GetGetMethod() != null || indexer.GetSetMethod() != null)
  30. {
  31. var paramType = indexParameters[0].ParameterType;
  32. if (_engine.ClrTypeConverter.TryConvert(key, paramType, CultureInfo.InvariantCulture, out _key))
  33. {
  34. _indexer = indexer;
  35. // get contains key method to avoid index exception being thrown in dictionaries
  36. paramTypeArray[0] = paramType;
  37. _containsKey = targetType.GetMethod("ContainsKey", paramTypeArray);
  38. break;
  39. }
  40. }
  41. }
  42. // throw if no indexer found
  43. if (_indexer == null)
  44. {
  45. ExceptionHelper.ThrowInvalidOperationException("No matching indexer found.");
  46. }
  47. Writable = true;
  48. }
  49. public IndexDescriptor(Engine engine, string key, object item)
  50. : this(engine, item.GetType(), key, item)
  51. {
  52. }
  53. protected internal override JsValue CustomValue
  54. {
  55. get
  56. {
  57. var getter = _indexer.GetGetMethod();
  58. if (getter == null)
  59. {
  60. ExceptionHelper.ThrowInvalidOperationException("Indexer has no public getter.");
  61. }
  62. object[] parameters = {_key};
  63. if (_containsKey != null)
  64. {
  65. if ((_containsKey.Invoke(_item, parameters) as bool?) != true)
  66. {
  67. return JsValue.Undefined;
  68. }
  69. }
  70. try
  71. {
  72. return JsValue.FromObject(_engine, getter.Invoke(_item, parameters));
  73. }
  74. catch
  75. {
  76. return JsValue.Undefined;
  77. }
  78. }
  79. set
  80. {
  81. var setter = _indexer.GetSetMethod();
  82. if (setter == null)
  83. {
  84. ExceptionHelper.ThrowInvalidOperationException("Indexer has no public setter.");
  85. }
  86. object[] parameters = {_key, value?.ToObject()};
  87. setter.Invoke(_item, parameters);
  88. }
  89. }
  90. }
  91. }