﻿using System.Globalization;
using System.Reflection;
using Jint.Native;

namespace Jint.Runtime.Descriptors.Specialized
{
    public sealed class IndexDescriptor : PropertyDescriptor
    {
        private readonly Engine _engine;
        private readonly object _key;
        private readonly object _item;
        private readonly MethodInfo _getter;
        private readonly MethodInfo _setter;

        public IndexDescriptor(Engine engine, string key, object item)
        {
            _engine = engine;
            _item = item;

            _getter = item.GetType().GetMethod("get_Item", BindingFlags.Instance | BindingFlags.Public);
            _setter = item.GetType().GetMethod("set_Item", BindingFlags.Instance | BindingFlags.Public);

            _key = _engine.Options.GetTypeConverter().Convert(key, _getter.GetParameters()[0].ParameterType, CultureInfo.InvariantCulture);

            Writable = true;
        }

        public override JsValue? Value
        {
            get
            {
                object[] parameters = { _key };
                return JsValue.FromObject(_engine, _getter.Invoke(_item, parameters));
            }

            set
            {
                var defaultValue = _item.GetType().IsValueType ? System.Activator.CreateInstance(_item.GetType()) : null;
                object[] parameters = { _key, value.HasValue ? value.Value.ToObject() : null };
                _setter.Invoke(_item, parameters);
            }
        }
    }
}
﻿using System;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Jint.Native;

namespace Jint.Runtime.Descriptors.Specialized
{
    public sealed class IndexDescriptor : PropertyDescriptor
    {
        private readonly Engine _engine;
        private readonly object _key;
        private readonly object _item;
        private readonly PropertyInfo _indexer;

        public IndexDescriptor(Engine engine, string key, object item)
        {
            _engine = engine;
            _item = item;

            // get all instance indexers with exactly 1 argument
            var indexers = item
                .GetType()
                .GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
                .Where(x => x.GetIndexParameters().Length == 1);
            
            // try to find first indexer having either public getter or setter with matching argument type
            foreach (var indexer in indexers)
            {
                if (indexer.GetGetMethod() != null || indexer.GetSetMethod() != null)
                {
                    var paramType = indexer.GetIndexParameters()[0].ParameterType;
                    try
                    {
                        _key = _engine.Options.GetTypeConverter().Convert(key, paramType, CultureInfo.InvariantCulture);
                        _indexer = indexer;
                        break;
                    }
                    catch { }
                }
            }

            // throw if no indexer found
            if(_indexer == null)
                throw new InvalidOperationException("No matching indexer found.");

            Writable = true;
        }

        public override JsValue? Value
        {
            get
            {
                var getter = _indexer.GetGetMethod();
                if(getter == null)
                    throw new InvalidOperationException("Indexer has no public getter.");

                object[] parameters = { _key };
                return JsValue.FromObject(_engine, getter.Invoke(_item, parameters));
            }

            set
            {
                var setter = _indexer.GetSetMethod();
                if (setter == null)
                    throw new InvalidOperationException("Indexer has no public setter.");

                object[] parameters = { _key, value.HasValue ? value.Value.ToObject() : null };
                setter.Invoke(_item, parameters);
            }
        }
    }
}
Fix AmbiguousMatchException in multiple indexers, make getters and setters invocation consistent
Fix AmbiguousMatchException in multiple indexers, make getters and setters invocation consistent

If class has more than 1 public indexers, IndexDescriptor ctor throws
AmbiguousMatchException.
If class has a public getter, and another public setter, its handled
inconsistently i.e. you will read from one property and write to another
one.
