This is an automated email from the ASF dual-hosted git repository. emilles pushed a commit to branch GROOVY-11639 in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 6d0622b2fbc3e2fbd4431704947bd64daf017a89 Author: Eric Milles <[email protected]> AuthorDate: Sat Apr 26 17:59:57 2025 -0500 GROOVY-11639: include super interface fields in face's property index --- src/main/java/groovy/lang/MetaClassImpl.java | 49 +++++++++++----------- .../codehaus/groovy/reflection/CachedClass.java | 2 +- .../groovy/lang/DefaultInterfaceMethodsTest.groovy | 27 +++++++++++- 3 files changed, 52 insertions(+), 26 deletions(-) diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java index 495639eac8..168c781216 100644 --- a/src/main/java/groovy/lang/MetaClassImpl.java +++ b/src/main/java/groovy/lang/MetaClassImpl.java @@ -2310,24 +2310,15 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass { */ private void setUpProperties(final PropertyDescriptor[] propertyDescriptors) { if (theCachedClass.isInterface) { - CachedClass superClass = ReflectionCache.OBJECT_CLASS; - List<CachedClass> superInterfaces = new ArrayList<>(theCachedClass.getInterfaces()); - superInterfaces.remove(theCachedClass); // always includes interface theCachedClass - // sort interfaces so that we may ensure a deterministic behaviour in case of - // ambiguous fields -- class implementing two interfaces using the same field - if (superInterfaces.size() > 1) { - superInterfaces.sort(CACHED_CLASS_NAME_COMPARATOR); + for (CachedClass iface : theCachedClass.getInterfaces()) { // includes theCachedClass + classPropertyIndex.computeIfAbsent(iface, x -> { + var index = new LinkedHashMap<String, MetaProperty>(); + addConsts(iface, index); + return index; + }); } - - Map<String, MetaProperty> iPropertyIndex = classPropertyIndex.computeIfAbsent(theCachedClass, x -> new LinkedHashMap<>()); - for (CachedClass sInterface : superInterfaces) { - Map<String, MetaProperty> sPropertyIndex = classPropertyIndex.computeIfAbsent(sInterface, x -> new LinkedHashMap<>()); - copyNonPrivateFields(sPropertyIndex, iPropertyIndex, null); - addFields(sInterface, iPropertyIndex); - } - addFields(theCachedClass, iPropertyIndex); - applyPropertyDescriptors(propertyDescriptors); + CachedClass superClass = ReflectionCache.OBJECT_CLASS; applyStrayPropertyMethods(superClass, classPropertyIndex.computeIfAbsent(superClass, x -> new LinkedHashMap<>()), true); } else { List<CachedClass> superClasses = getSuperClasses(); @@ -2339,7 +2330,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass { } if (theCachedClass.isArray) { // add the special read-only "length" property - LinkedHashMap<String, MetaProperty> map = new LinkedHashMap<>(); + var map = new LinkedHashMap<String, MetaProperty>(); map.put("length", arrayLengthProperty); classPropertyIndex.put(theCachedClass, map); } @@ -2437,12 +2428,15 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass { } private void inheritStaticInterfaceFields(List<CachedClass> superClasses, Iterable<CachedClass> interfaces) { - for (CachedClass iclass : interfaces) { - LinkedHashMap<String, MetaProperty> iPropertyIndex = classPropertyIndex.computeIfAbsent(iclass, k -> new LinkedHashMap<>()); - addFields(iclass, iPropertyIndex); + for (CachedClass iface : interfaces) { + LinkedHashMap<String, MetaProperty> iPropertyIndex = classPropertyIndex.computeIfAbsent(iface, x -> { + var index = new LinkedHashMap<String, MetaProperty>(); + addConsts(iface, index); + return index; + }); for (CachedClass superClass : superClasses) { - if (!iclass.getTheClass().isAssignableFrom(superClass.getTheClass())) continue; - LinkedHashMap<String, MetaProperty> sPropertyIndex = classPropertyIndex.computeIfAbsent(superClass, k -> new LinkedHashMap<>()); + if (!iface.getTheClass().isAssignableFrom(superClass.getTheClass())) continue; + LinkedHashMap<String, MetaProperty> sPropertyIndex = classPropertyIndex.computeIfAbsent(superClass, x -> new LinkedHashMap<>()); copyNonPrivateFields(iPropertyIndex, sPropertyIndex, null); } } @@ -2462,9 +2456,16 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass { } } - private static void addFields(CachedClass klass, Map<String, MetaProperty> propertyIndex) { + private static void addConsts(final CachedClass iface, final Map<String, MetaProperty> index) { + for (CachedClass superInterface : iface.getDeclaredInterfaces()) { // GROOVY-11639 + addConsts(superInterface, index); + } + addFields(iface, index); + } + + private static void addFields(final CachedClass klass, final Map<String, MetaProperty> index) { for (CachedField field : klass.getFields()) { - propertyIndex.put(field.getName(), field); + index.put(field.getName(), field); } } diff --git a/src/main/java/org/codehaus/groovy/reflection/CachedClass.java b/src/main/java/org/codehaus/groovy/reflection/CachedClass.java index cc9a27b3df..5a89b1a0fc 100644 --- a/src/main/java/org/codehaus/groovy/reflection/CachedClass.java +++ b/src/main/java/org/codehaus/groovy/reflection/CachedClass.java @@ -174,7 +174,7 @@ public class CachedClass { @Override public Set<CachedClass> initValue() { Class[] classes = getTheClass().getInterfaces(); - Set<CachedClass> res = new HashSet<>(classes.length); + Set<CachedClass> res = new LinkedHashSet<>(classes.length); for (Class cls : classes) { res.add(ReflectionCache.getCachedClass(cls)); } diff --git a/src/test/groovy/groovy/lang/DefaultInterfaceMethodsTest.groovy b/src/test/groovy/groovy/lang/DefaultInterfaceMethodsTest.groovy index 82e3ab4ea3..bcd1bab7db 100644 --- a/src/test/groovy/groovy/lang/DefaultInterfaceMethodsTest.groovy +++ b/src/test/groovy/groovy/lang/DefaultInterfaceMethodsTest.groovy @@ -18,7 +18,7 @@ */ package groovy.lang -import org.junit.Test +import org.junit.jupiter.api.Test import static groovy.test.GroovyAssert.assertScript @@ -58,4 +58,29 @@ final class DefaultInterfaceMethodsTest { assert new C().m() == 'Bon jour' ''' } + + // GROOVY-11639 + @Test + void testDefaultMethodUsesSuperProperty() { + assertScript ''' + interface I { + String FOO = 'foo' + } + + interface J extends I { + default m() { + return FOO + 'bar' + } + } + + class C implements J { + def xx() { + return m() + 'baz' + } + } + + String result = new C().xx() + assert result == 'foobarbaz' + ''' + } }
