浏览代码

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>
tags/V1_9_20_1
Alexander Kriegisch 9 个月前
父节点
当前提交
e2a355b379

+ 59
- 0
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();
}
}

二进制
tests/bugs1921/gh_spring_27761/JpaRepository_bridge_first.jar 查看文件


+ 35
- 0
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;
}
}

+ 12
- 0
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);
}

+ 11
- 0
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>

正在加载...
取消
保存