Browse Source

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.
h15ter 11 years ago
parent
commit
72c5698f12
1 changed files with 38 additions and 9 deletions
  1. 38 9
      Jint/Runtime/Descriptors/Specialized/IndexDescriptor.cs

+ 38 - 9
Jint/Runtime/Descriptors/Specialized/IndexDescriptor.cs

@@ -1,4 +1,6 @@
-using System.Globalization;
+using System;
+using System.Globalization;
+using System.Linq;
 using System.Reflection;
 using Jint.Native;
 
@@ -9,18 +11,38 @@ namespace Jint.Runtime.Descriptors.Specialized
         private readonly Engine _engine;
         private readonly object _key;
         private readonly object _item;
-        private readonly MethodInfo _getter;
-        private readonly MethodInfo _setter;
+        private readonly PropertyInfo _indexer;
 
         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);
+            // 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 { }
+                }
+            }
 
-            _key = _engine.Options.GetTypeConverter().Convert(key, _getter.GetParameters()[0].ParameterType, CultureInfo.InvariantCulture);
+            // throw if no indexer found
+            if(_indexer == null)
+                throw new InvalidOperationException("No matching indexer found.");
 
             Writable = true;
         }
@@ -29,15 +51,22 @@ namespace Jint.Runtime.Descriptors.Specialized
         {
             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));
+                return JsValue.FromObject(_engine, getter.Invoke(_item, parameters));
             }
 
             set
             {
-                var defaultValue = _item.GetType().IsValueType ? System.Activator.CreateInstance(_item.GetType()) : null;
+                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);
+                setter.Invoke(_item, parameters);
             }
         }
     }