123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Reflection;
- using Jint.Native;
- namespace Jint.Runtime.Interop.Reflection
- {
- internal sealed class IndexerAccessor : ReflectionAccessor
- {
- private readonly object _key;
- private readonly PropertyInfo _indexer;
- private readonly MethodInfo _getter;
- private readonly MethodInfo _setter;
- private readonly MethodInfo _containsKey;
- private static readonly PropertyInfo _iListIndexer = typeof(IList).GetProperty("Item");
- private IndexerAccessor(PropertyInfo indexer, MethodInfo containsKey, object key)
- : base(indexer.PropertyType, key)
- {
- _indexer = indexer;
- _containsKey = containsKey;
- _key = key;
- _getter = indexer.GetGetMethod();
- _setter = indexer.GetSetMethod();
- }
- internal static bool TryFindIndexer(
- Engine engine,
- Type targetType,
- string propertyName,
- out IndexerAccessor indexerAccessor,
- out PropertyInfo indexer)
- {
- var paramTypeArray = new Type[1];
- IndexerAccessor ComposeIndexerFactory(PropertyInfo candidate, Type paramType)
- {
- if (engine.ClrTypeConverter.TryConvert(propertyName, paramType, CultureInfo.InvariantCulture, out var key))
- {
- // the key can be converted for this indexer
- var indexerProperty = candidate;
- // get contains key method to avoid index exception being thrown in dictionaries
- paramTypeArray[0] = paramType;
- var containsKeyMethod = targetType.GetMethod(nameof(IDictionary<string, string>.ContainsKey), paramTypeArray);
- if (containsKeyMethod is null)
- {
- paramTypeArray[0] = typeof(object);
- containsKeyMethod = targetType.GetMethod(nameof(IDictionary.Contains), paramTypeArray);
- }
- return new IndexerAccessor(indexerProperty, containsKeyMethod, key);
- }
- // the key type doesn't work for this indexer
- return null;
- }
- // default indexer wins
- if (typeof(IList).IsAssignableFrom(targetType))
- {
- indexerAccessor = ComposeIndexerFactory(_iListIndexer, typeof(int));
- if (indexerAccessor != null)
- {
- indexer = _iListIndexer;
- return true;
- }
- }
- // try to find first indexer having either public getter or setter with matching argument type
- foreach (var candidate in targetType.GetProperties())
- {
- var indexParameters = candidate.GetIndexParameters();
- if (indexParameters.Length != 1)
- {
- continue;
- }
- if (candidate.GetGetMethod() != null || candidate.GetSetMethod() != null)
- {
- var paramType = indexParameters[0].ParameterType;
- indexerAccessor = ComposeIndexerFactory(candidate, paramType);
- if (indexerAccessor != null)
- {
- indexer = candidate;
- return true;
- }
- }
- }
- indexerAccessor = default;
- indexer = default;
- return false;
- }
- public override bool Writable => _indexer.CanWrite;
- protected override object DoGetValue(object target)
- {
- if (_getter is null)
- {
- ExceptionHelper.ThrowInvalidOperationException("Indexer has no public getter.");
- return null;
- }
- object[] parameters = {_key};
- if (_containsKey != null)
- {
- if (_containsKey.Invoke(target, parameters) as bool? != true)
- {
- return JsValue.Undefined;
- }
- }
- return _getter.Invoke(target, parameters);
- }
- protected override void DoSetValue(object target, object value)
- {
- if (_setter is null)
- {
- ExceptionHelper.ThrowInvalidOperationException("Indexer has no public setter.");
- }
- object[] parameters = {_key, value};
- _setter!.Invoke(target, parameters);
- }
- }
- }
|