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'
+        '''
+    }
 }

Reply via email to