You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2026/04/18 00:23:24 UTC
(groovy) 01/01: GROOVY-11929: drop abstract methods from methodsForSuper if no MOP match
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch GROOVY-11929
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit d0030175c8eeb0ab78824b59adeea6663d4cb118
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Apr 17 19:11:49 2026 -0500
GROOVY-11929: drop abstract methods from methodsForSuper if no MOP match
---
src/main/java/groovy/lang/MetaClassImpl.java | 12 +++--
.../groovy/runtime/metaclass/MetaMethodIndex.java | 10 ++--
src/test/groovy/bugs/Groovy11929.groovy | 58 ++++++++++++++++++++++
3 files changed, 72 insertions(+), 8 deletions(-)
diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java
index 3afaf40f47..7fb33f371c 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -518,8 +518,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
int matchedMethod = mopArrayIndex(method, c);
if (matchedMethod >= 0) {
methods.set(i, mopMethods[matchedMethod]);
- } else if (!useThis && !isDGM(method) && (isBridge(method)
- || c == method.getDeclaringClass().getTheClass())) {
+ } else if (!useThis && notSuperUseful(method, c)) {
methods.remove(i--); // not fit for super usage
}
}
@@ -534,8 +533,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
if (matchedMethod >= 0) {
if (useThis) e.methods = mopMethods[matchedMethod];
else e.methodsForSuper = mopMethods[matchedMethod];
- } else if (!useThis && !isDGM(method) && (isBridge(method)
- || c == method.getDeclaringClass().getTheClass())) {
+ } else if (!useThis && notSuperUseful(method, c)) {
e.methodsForSuper = null; // not fit for super usage
}
}
@@ -576,6 +574,12 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
return -1;
}
+ private boolean notSuperUseful(final MetaMethod method, final Class<?> c) {
+ return !isDGM(method) && (isBridge(method)
+ || method.isAbstract() // GROOVY-11929
+ || c == method.getDeclaringClass().getTheClass());
+ }
+
private boolean isBridge(final MetaMethod method) {
return (method.getModifiers() & Opcodes.ACC_BRIDGE) != 0;
}
diff --git a/src/main/java/org/codehaus/groovy/runtime/metaclass/MetaMethodIndex.java b/src/main/java/org/codehaus/groovy/runtime/metaclass/MetaMethodIndex.java
index 983baacc44..57df90a415 100644
--- a/src/main/java/org/codehaus/groovy/runtime/metaclass/MetaMethodIndex.java
+++ b/src/main/java/org/codehaus/groovy/runtime/metaclass/MetaMethodIndex.java
@@ -207,13 +207,15 @@ public class MetaMethodIndex {
* should be kept.
*/
private static boolean isOverridden(final MetaMethod inIndex, final MetaMethod toIndex) {
- // do not overwrite private methods
- if (inIndex.isPrivate()) return false;
+ // don't overwrite private method
+ if (inIndex.isPrivate()) {
+ return false;
+ }
CachedClass inIndexDC = inIndex.getDeclaringClass();
CachedClass toIndexDC = toIndex.getDeclaringClass();
if (inIndexDC == toIndexDC) {
- return isNonRealMethod(toIndex) || inIndex.isSynthetic(); // GROOVY-10136, GROOVY-10594
+ return inIndex.isSynthetic() || isNonRealMethod(toIndex); // GROOVY-10136, GROOVY-10594
}
// interface vs instance method; be careful...
@@ -221,7 +223,7 @@ public class MetaMethodIndex {
&& inIndexDC.isInterface() != toIndexDC.isInterface()
&& !(toIndex instanceof ClosureMetaMethod || toIndex instanceof ClosureStaticMetaMethod)) { // GROOVY-3493
// this is the old logic created for GROOVY-2391 and GROOVY-7879, which was labeled as "do not overwrite interface methods with instance methods"
- return (isNonRealMethod(inIndex) || !inIndexDC.isInterface() || toIndexDC.isInterface()) && !toIndexDC.isAssignableFrom(inIndexDC.getTheClass());
+ return (toIndexDC.isInterface() || !inIndexDC.isInterface() || isNonRealMethod(inIndex)) && !toIndexDC.isAssignableFrom(inIndexDC.getTheClass());
}
// prefer most-specific or most-recent for type disjunction
diff --git a/src/test/groovy/bugs/Groovy11929.groovy b/src/test/groovy/bugs/Groovy11929.groovy
new file mode 100644
index 0000000000..06429a47dd
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy11929.groovy
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package bugs
+
+import org.junit.jupiter.api.Test
+
+import static groovy.test.GroovyAssert.assertScript
+
+final class Groovy11929 {
+
+ @Test
+ void testInvokeSpecial() {
+ assertScript '''
+ interface Dao<T> {
+ T save(T t)
+ }
+
+ abstract class AbstractDao<T> implements Dao<T> {
+ @Override
+ T save(T t) { t }
+ }
+
+ interface EntityDao extends Dao<Entity> {
+ @Override
+ Entity save(Entity entity) // not replaced by super$2$save(Object)
+ }
+
+ class EntityDaoImpl extends AbstractDao<Entity> implements EntityDao {
+ @Override
+ Entity save(Entity entity) { super.save(entity) }
+ }
+
+ class Entity {
+ long id
+ }
+
+ def entity = new Entity()
+ def dao = new EntityDaoImpl()
+ assert dao.save(entity) === entity
+ '''
+ }
+}