aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorAlexander Kriegisch <Alexander@Kriegisch.name>2023-08-21 17:18:27 +0700
committerAlexander Kriegisch <Alexander@Kriegisch.name>2023-08-21 17:58:47 +0700
commite2a355b37935faf9a8de1ce7ca1f134b5a9df891 (patch)
treecc80dbc345e21c00c897bf8a1c6cadbc0659e107 /tests
parente6c06bdd6ad18171efe5c76b83b62c247832a156 (diff)
downloadaspectj-e2a355b37935faf9a8de1ce7ca1f134b5a9df891.tar.gz
aspectj-e2a355b37935faf9a8de1ce7ca1f134b5a9df891.zip
Regression test for Spring issue 27761
Relates to spring-projects/spring-framework#27761. The test needs an ASM-generated class file with reordered methods in order to reproduce the issue in plain AspectJ. The test fails now, but should pass after the fix. Signed-off-by: Alexander Kriegisch <Alexander@Kriegisch.name>
Diffstat (limited to 'tests')
-rw-r--r--tests/bugs1921/gh_spring_27761/JpaRepositoryDump.java59
-rw-r--r--tests/bugs1921/gh_spring_27761/JpaRepository_bridge_first.jarbin0 -> 456 bytes
-rw-r--r--tests/bugs1921/gh_spring_27761/RepositoryAspect.aj35
-rw-r--r--tests/src/test/java/org/aspectj/systemtest/ajc1920/Bugs1920Tests.java12
-rw-r--r--tests/src/test/resources/org/aspectj/systemtest/ajc1920/ajc1920.xml11
5 files changed, 117 insertions, 0 deletions
diff --git a/tests/bugs1921/gh_spring_27761/JpaRepositoryDump.java b/tests/bugs1921/gh_spring_27761/JpaRepositoryDump.java
new file mode 100644
index 000000000..a007abe96
--- /dev/null
+++ b/tests/bugs1921/gh_spring_27761/JpaRepositoryDump.java
@@ -0,0 +1,59 @@
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+class JpaRepositoryDump implements Opcodes {
+ public static void main(String[] args) throws IOException {
+ try (FileOutputStream outputStream = new FileOutputStream("JpaRepository.class")) {
+ outputStream.write(dump());
+ }
+ }
+
+ public static byte[] dump() {
+ /*
+ Write out a class corresponding to this source code:
+ ------------------------------------------------------------
+ interface JpaRepository<T> extends CrudRepository<T> {
+ @Override
+ <S extends T> List<S> saveAll(Iterable<S> entities);
+ }
+ ------------------------------------------------------------
+ The only difference to the original class created by Javac or Ajc is that the bridge method is written to the class
+ file first, then the overriding method with the return type narrowed from Iterable to List. This has the effect of
+ org.aspectj.weaver.ResolvedType.getMethodsIncludingIntertypeDeclarations also finding the bridge method first,
+ which helps to reproduce https://github.com/spring-projects/spring-framework/issues/27761 in a regression test.
+
+ The resulting class file can be found in .../gh_spring_27761/JpaRepository_bridge_first.jar and is used during test
+ "do not match bridge methods".
+ */
+
+ ClassWriter classWriter = new ClassWriter(0);
+ classWriter.visit(V1_8, ACC_ABSTRACT | ACC_INTERFACE, "JpaRepository", "<T:Ljava/lang/Object;>Ljava/lang/Object;LCrudRepository<TT;>;", "java/lang/Object", new String[]{"CrudRepository"});
+ classWriter.visitSource("RepositoryAspect.aj", null);
+
+ MethodVisitor methodVisitor;
+ methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_BRIDGE | ACC_SYNTHETIC, "saveAll", "(Ljava/lang/Iterable;)Ljava/lang/Iterable;", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(1, label0);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ methodVisitor.visitTypeInsn(CHECKCAST, "java/lang/Iterable");
+ methodVisitor.visitMethodInsn(INVOKEINTERFACE, "JpaRepository", "saveAll", "(Ljava/lang/Iterable;)Ljava/util/List;", true);
+ methodVisitor.visitInsn(ARETURN);
+ methodVisitor.visitMaxs(2, 2);
+ methodVisitor.visitEnd();
+
+ methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "saveAll", "(Ljava/lang/Iterable;)Ljava/util/List;", "<S:TT;>(Ljava/lang/Iterable<TS;>;)Ljava/util/List<TS;>;", null);
+ methodVisitor.visitEnd();
+
+ classWriter.visitEnd();
+
+ return classWriter.toByteArray();
+ }
+}
diff --git a/tests/bugs1921/gh_spring_27761/JpaRepository_bridge_first.jar b/tests/bugs1921/gh_spring_27761/JpaRepository_bridge_first.jar
new file mode 100644
index 000000000..47aa5c053
--- /dev/null
+++ b/tests/bugs1921/gh_spring_27761/JpaRepository_bridge_first.jar
Binary files differ
diff --git a/tests/bugs1921/gh_spring_27761/RepositoryAspect.aj b/tests/bugs1921/gh_spring_27761/RepositoryAspect.aj
new file mode 100644
index 000000000..b938d754c
--- /dev/null
+++ b/tests/bugs1921/gh_spring_27761/RepositoryAspect.aj
@@ -0,0 +1,35 @@
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public aspect RepositoryAspect {
+ Object around(): execution(* JpaRepository.save*(..)) {
+ System.out.println(thisJoinPoint);
+ return proceed();
+ }
+
+ public static void main(String[] args) {
+ new RepositoryImpl<String>().saveAll(Arrays.asList("One", "Two", "Three"));
+ }
+}
+
+interface CrudRepository<T> {
+ <S extends T> Iterable<S> saveAll(Iterable<S> entities);
+}
+
+/*
+interface JpaRepository<T> extends CrudRepository<T> {
+ @Override
+ <S extends T> List<S> saveAll(Iterable<S> entities);
+}
+*/
+
+class RepositoryImpl<S> implements JpaRepository<String> {
+ @Override
+ public <S extends String> List<S> saveAll(Iterable<S> entities) {
+ List<S> entityList = new ArrayList<>();
+ entities.iterator().forEachRemaining(entityList::add);
+ System.out.println("Saving " + entityList);
+ return entityList;
+ }
+}
diff --git a/tests/src/test/java/org/aspectj/systemtest/ajc1920/Bugs1920Tests.java b/tests/src/test/java/org/aspectj/systemtest/ajc1920/Bugs1920Tests.java
index 574f1b7aa..e1f3fa1ca 100644
--- a/tests/src/test/java/org/aspectj/systemtest/ajc1920/Bugs1920Tests.java
+++ b/tests/src/test/java/org/aspectj/systemtest/ajc1920/Bugs1920Tests.java
@@ -67,6 +67,18 @@ public class Bugs1920Tests extends XMLBasedAjcTestCase {
runTest("correctly handle overloaded private methods in aspects");
}
+ /**
+ * If one generic method overrides another one with a narrower return type, avoid matching bridge methods.
+ * <p>
+ * See <a href="https://github.com/spring-projects/spring-framework/issues/27761">Spring GitHub issue 27761</a>.
+ * <p>
+ * This test uses an ASM-modified class file reproducing the problem seen in Spring in plain AspectJ. Before the
+ * bugfix, it fails with <b>"advice defined in RepositoryAspect has not been applied [Xlint:adviceDidNotMatch]".</b>
+ */
+ public void test_Spring_GitHub_27761() {
+ runTest("do not match bridge methods");
+ }
+
public static Test suite() {
return XMLBasedAjcTestCase.loadSuite(Bugs1920Tests.class);
}
diff --git a/tests/src/test/resources/org/aspectj/systemtest/ajc1920/ajc1920.xml b/tests/src/test/resources/org/aspectj/systemtest/ajc1920/ajc1920.xml
index 402f50a2f..db47e073d 100644
--- a/tests/src/test/resources/org/aspectj/systemtest/ajc1920/ajc1920.xml
+++ b/tests/src/test/resources/org/aspectj/systemtest/ajc1920/ajc1920.xml
@@ -418,4 +418,15 @@
</run>
</ajc-test>
+ <!-- https://github.com/spring-projects/spring-framework/issues/27761 -->
+ <ajc-test dir="bugs1921/gh_spring_27761" vm="8" title="do not match bridge methods">
+ <compile files="RepositoryAspect.aj" inpath="JpaRepository_bridge_first.jar" options="-8"/>
+ <run class="RepositoryAspect">
+ <stdout>
+ <line text="execution(List RepositoryImpl.saveAll(Iterable))"/>
+ <line text="Saving [One, Two, Three]"/>
+ </stdout>
+ </run>
+ </ajc-test>
+
</suite>