diff --git a/Jint.Tests/Runtime/InteropTests.cs b/Jint.Tests/Runtime/InteropTests.cs index 2775f77721..ac364f68b4 100644 --- a/Jint.Tests/Runtime/InteropTests.cs +++ b/Jint.Tests/Runtime/InteropTests.cs @@ -3283,5 +3283,53 @@ public void AccessingBaseTypeShouldBeEqualToAccessingDerivedType() Assert.True(res.AsBoolean()); } + + public interface IIndexer + { + T this[int index] { get; } + } + + public interface ICountable + { + int Count { get; } + } + + public interface IStringCollection : IIndexer, ICountable + { + string this[string name] { get; } + } + + public class Strings : IStringCollection + { + private readonly string[] _strings; + public Strings(string[] strings) + { + _strings = strings; + } + public string this[string name] + { + get + { + return int.TryParse(name, out var index) ? _strings[index] : _strings.FirstOrDefault(x => x.Contains(name)); + } + } + + public string this[int index] => _strings[index]; + public int Count => _strings.Length; + } + + public class Utils + { + public IStringCollection GetStrings() => new Strings(new [] { "a", "b", "c" }); + } + + [Fact] + public void AccessingInterfaceShouldContainExtendedInterfaces() + { + var engine = new Engine(); + engine.SetValue("Utils", new Utils()); + var result = engine.Evaluate("const strings = Utils.GetStrings(); strings.Count;").AsNumber(); + Assert.Equal(3, result); + } } } diff --git a/Jint/Runtime/Interop/TypeResolver.cs b/Jint/Runtime/Interop/TypeResolver.cs index 94de267184..16293f1633 100644 --- a/Jint/Runtime/Interop/TypeResolver.cs +++ b/Jint/Runtime/Interop/TypeResolver.cs @@ -212,29 +212,50 @@ internal bool TryFindMemberAccessor( PropertyInfo? property = null; var memberNameComparer = MemberNameComparer; var typeResolverMemberNameCreator = MemberNameCreator; - foreach (var p in type.GetProperties(bindingFlags)) + + PropertyInfo? GetProperty(Type t) { - if (!Filter(engine, p)) + foreach (var p in t.GetProperties(bindingFlags)) { - continue; - } + if (!Filter(engine, p)) + { + continue; + } - // only if it's not an indexer, we can do case-ignoring matches - var isStandardIndexer = p.GetIndexParameters().Length == 1 && p.Name == "Item"; - if (!isStandardIndexer) - { - foreach (var name in typeResolverMemberNameCreator(p)) + // only if it's not an indexer, we can do case-ignoring matches + var isStandardIndexer = p.GetIndexParameters().Length == 1 && p.Name == "Item"; + if (!isStandardIndexer) { - if (memberNameComparer.Equals(name, memberName)) + foreach (var name in typeResolverMemberNameCreator(p)) { - property = p; - break; + if (memberNameComparer.Equals(name, memberName)) + { + property = p; + break; + } } } } + + return property; + } + + property = GetProperty(type); + + if (property is null && type.IsInterface) + { + // check inherited interfaces + foreach (var iface in type.GetInterfaces()) + { + property = GetProperty(iface); + if (property is not null) + { + break; + } + } } - if (property != null) + if (property is not null) { accessor = new PropertyAccessor(memberName, property, indexerToTry); return true; @@ -259,7 +280,7 @@ internal bool TryFindMemberAccessor( } } - if (field != null) + if (field is not null) { accessor = new FieldAccessor(field, memberName, indexerToTry); return true;