This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new e484ac5a3e GROOVY-2582, GROOVY-11549: no bridge method for interface
default method
e484ac5a3e is described below
commit e484ac5a3e85b25bb64247b84f56a047606cc54b
Author: Eric Milles <[email protected]>
AuthorDate: Sun Feb 16 13:42:27 2025 -0600
GROOVY-2582, GROOVY-11549: no bridge method for interface default method
---
.../org/codehaus/groovy/classgen/Verifier.java | 31 ++++++++---
src/test/gls/invocation/CovariantReturnTest.groovy | 26 +++++----
src/test/groovy/bugs/Groovy4116Bug.groovy | 62 ++++++++--------------
3 files changed, 60 insertions(+), 59 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/classgen/Verifier.java
b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
index 195ac34d77..29bc833155 100644
--- a/src/main/java/org/codehaus/groovy/classgen/Verifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
@@ -1416,10 +1416,31 @@ public class Verifier implements GroovyClassVisitor,
Opcodes {
}
protected void addCovariantMethods(final ClassNode classNode) {
- // unimplemented abstract methods from interfaces
- Map<String, MethodNode> absInterfaceMethods =
ClassNodeUtils.getDeclaredMethodsFromInterfaces(classNode);
- Map<String, MethodNode> allInterfaceMethods = new
HashMap<>(absInterfaceMethods);
- ClassNodeUtils.addDeclaredMethodsFromAllInterfaces(classNode,
allInterfaceMethods);
+ Map<String, MethodNode> absInterfaceMethods = new HashMap<>();
+ Map<String, MethodNode> allInterfaceMethods = new HashMap<>();
+ Set<ClassNode> allInterfaces = getAllInterfaces(classNode);
+ allInterfaces.remove(classNode);
+
+ for (ClassNode in : allInterfaces) {
+ for (MethodNode mn : in.getMethods()) {
+ // interfaces may have private/static methods
+ if (mn.isPrivate() || mn.isStatic()) continue;
+
+ if (mn.isAbstract()) {
+ allInterfaceMethods.putIfAbsent(mn.getTypeDescriptor(),
mn);
+ } else { // GROOVY-11549: default method replaces an abstract
+ mn = allInterfaceMethods.put(mn.getTypeDescriptor(), mn);
+ if (mn != null && !mn.isAbstract())
+ allInterfaceMethods.put(mn.getTypeDescriptor(), mn);
+ }
+ }
+ }
+
+ for (Map.Entry<String, MethodNode> entry :
allInterfaceMethods.entrySet()) {
+ if (entry.getValue().isAbstract()/* ||
entry.getValue().isDefault()*/) {
+ absInterfaceMethods.putIfAbsent(entry.getKey(),
entry.getValue());
+ }
+ }
List<MethodNode> declaredMethods = new
ArrayList<>(classNode.getMethods());
for (Iterator<MethodNode> methodsIterator =
declaredMethods.iterator(); methodsIterator.hasNext(); ) {
@@ -1434,8 +1455,6 @@ public class Verifier implements GroovyClassVisitor,
Opcodes {
throw new RuntimeParserException("The method " + m.getName() +
" should be public as it implements the corresponding method from interface " +
interfaceMethod.getDeclaringClass(), sourceOf(m));
}
}
- // interfaces may have private/static methods in JDK9
- absInterfaceMethods.values().removeIf(interfaceMethod ->
interfaceMethod.isPrivate() || interfaceMethod.isStatic());
Map<String, MethodNode> methodsToAdd = new HashMap<>();
Map<String, ClassNode > genericsSpec = Collections.emptyMap();
diff --git a/src/test/gls/invocation/CovariantReturnTest.groovy
b/src/test/gls/invocation/CovariantReturnTest.groovy
index 98510ce6f7..e90a29fd12 100644
--- a/src/test/gls/invocation/CovariantReturnTest.groovy
+++ b/src/test/gls/invocation/CovariantReturnTest.groovy
@@ -146,20 +146,18 @@ final class CovariantReturnTest {
assert c.foo() == ""
'''
- // basically the same as above, but with an example from an error
report
- // Properties has a method "String getProperty(String)", this class
- // is also a GroovyObject, meaning a "Object getProperty(String)"
method
- // should be implemented. But this method should not be the usual
automatically
- // added getProperty, but a bridge to the getProperty method provided
by Properties
- assertScript '''
- class Configuration extends java.util.Properties {
- }
-
- assert Configuration.declaredMethods.count{ it.name=="getProperty"
} == 1
- def conf = new Configuration()
- conf.setProperty("a","b")
- // the following assert would fail if standard getProperty method
was added by the compiler
- assert conf.getProperty("a") == "b"
+ // an example from an error report
+ // Properties has a method "String getProperty(String)"
+ // GroovyObject has a default method "Object getProperty(String)"
+ assertScript '''import static groovy.test.GroovyAssert.shouldFail
+ class C extends Properties { }
+ shouldFail(NoSuchMethodException) {
+ C.getDeclaredMethod("getProperty")
+ }
+
+ def c = new C()
+ c.setProperty("a", "b")
+ assert c.getProperty("a") == "b" // fails if standard method added
by the compiler
'''
assertScript '''
diff --git a/src/test/groovy/bugs/Groovy4116Bug.groovy
b/src/test/groovy/bugs/Groovy4116Bug.groovy
index 4821e8993a..9297542952 100644
--- a/src/test/groovy/bugs/Groovy4116Bug.groovy
+++ b/src/test/groovy/bugs/Groovy4116Bug.groovy
@@ -21,47 +21,31 @@ package groovy.bugs
import groovy.test.GroovyTestCase
import org.codehaus.groovy.control.MultipleCompilationErrorsException
-class Groovy4116Bug extends GroovyTestCase {
- void testAnInterfaceMethodNotImplementedPublic() {
- try {
- new GroovyShell().parse """
- class C4116 implements I4116 {
- protected foo() {}
- }
-
- interface I4116 {
- def foo()
- }
+final class Groovy4116Bug extends GroovyTestCase {
- def c = new C4116()
- c.foo()
- """
- fail('The compilation should have failed as the interface method
is not implemented as public')
- } catch (MultipleCompilationErrorsException e) {
- def syntaxError = e.errorCollector.getSyntaxError(0)
- assert syntaxError.message.contains('The method foo should be
public as it implements the corresponding method from interface I4116')
- }
+ void testAnInterfaceMethodNotImplementedPublic() {
+ def err = shouldFail MultipleCompilationErrorsException, '''
+ class C4116 implements I4116 {
+ protected foo() {}
+ }
+ interface I4116 {
+ def foo()
+ }
+ '''
+ assert err =~ /The method foo should be public as it implements the
corresponding method from interface I4116/
}
void testAnInterfaceMethodNotImplementedPublicV2SuperClassInterface() {
- try {
- new GroovyShell().parse """
- abstract class S4116V2 implements I4116V2 { }
- class C4116V2 extends S4116V2 {
- protected foo() {}
- }
-
- interface I4116V2 {
- def foo()
- }
-
- def c = new C4116V2()
- c.foo()
- """
- fail('The compilation should have failed as the interface method
is not implemented as public')
- } catch (MultipleCompilationErrorsException e) {
- def syntaxError = e.errorCollector.getSyntaxError(0)
- assert syntaxError.message.contains('The method foo should be
public as it implements the corresponding method from interface I4116')
- }
+ def err = shouldFail MultipleCompilationErrorsException, '''
+ abstract class A4116 implements I4116 {
+ }
+ class C4116 extends A4116 {
+ protected foo() {}
+ }
+ interface I4116 {
+ def foo()
+ }
+ '''
+ assert err =~ /The method foo should be public as it implements the
corresponding method from interface I4116/
}
-}
\ No newline at end of file
+}