--- /dev/null
+import java.util.Arrays;
+
+public aspect NegatedTypeAspect {
+ before(): execution(!void get*()) {
+ System.out.println("[GETTER] " + thisJoinPoint);
+ }
+
+ before(): execution(!String get*()) {
+ System.out.println("[NON-STRING GETTER] " + thisJoinPoint);
+ }
+
+ before(): execution(String[] get*()) {
+ System.out.println("[STRING-ARRAY GETTER] " + thisJoinPoint);
+ }
+
+ before(): execution(!String[] get*()) {
+ System.out.println("[NON-STRING-ARRAY GETTER] " + thisJoinPoint);
+ }
+
+ before(): execution(!String[][] get*()) {
+ System.out.println("[NON-STRING-ARRAY-ARRAY GETTER] " + thisJoinPoint);
+ }
+
+ before(): execution(void set*(*)) {
+ System.out.println("[SETTER] " + thisJoinPoint);
+ }
+
+ public static void main(String[] args) {
+ Person person = new Person();
+ person.setId(11);
+ person.setFirstName("Marie");
+ person.setLastName("Curie");
+ System.out.println(person);
+ person.getId();
+ person.getFirstName();
+ person.getLastName();
+ System.out.println(person.getFullName(false));
+ person.setFullName("Albert Einstein");
+ person.setId(22);
+ System.out.println(person);
+ System.out.println(person.getFullName(true));
+ person.getVoid();
+ System.out.println(Arrays.deepToString(person.getStringArray()));
+ System.out.println(Arrays.deepToString(person.getStringArrayArray()));
+ System.out.println(person.setSomething("something"));
+ }
+}
+
+class Person {
+ private int id;
+ private String lastName;
+ private String firstName;
+
+ // Bean getters/setters, matched by aspect
+
+ // Non-string getter, matched by corresponding pointcut
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ // Non-string getter (String[] != String)
+ public String[] getStringArray() {
+ return new String[] {"Hello", "world"};
+ }
+
+ // Non-string, non-string-array getter (String[] != String, String[] != String[][])
+ public String[][] getStringArrayArray() {
+ return new String[][] {{"Hello", "world"}, {"Hallo", "Welt"}};
+ }
+
+ // Non-bean getters/setters, not matched by aspect
+
+ public String getFullName(boolean lastNameFirst) {
+ return lastNameFirst
+ ? lastName + ", " + firstName
+ : firstName + " " + lastName;
+ }
+
+ public void setFullName(String fullName) {
+ boolean lastNameFirst = fullName.contains(",");
+ String[] nameParts = fullName.split("[, ]+");
+ if (lastNameFirst) {
+ firstName = nameParts[1];
+ lastName = nameParts[0];
+ } else {
+ firstName = nameParts[0];
+ lastName = nameParts[1];
+ }
+ }
+
+ public String setSomething(String something) {
+ return "AspectJ rules!";
+ }
+
+ // Non-string getter, matched by corresponding pointcut
+ public void getVoid() {}
+
+ // Other methods, not matched by aspect
+
+ @Override
+ public String toString() {
+ return "Person(" + "id=" + id + ", lastName='" + lastName + '\'' + ", firstName='" + firstName + '\'' + ')';
+ }
+}
--- /dev/null
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class JpaRepositoryDump implements Opcodes {
+ public static void main(String[] args) throws IOException {
+ // Write class file to test sandbox directory
+ String classFile = args[0] + File.separator + "JpaRepository.class";
+ try (FileOutputStream outputStream = new FileOutputStream(classFile)) {
+ 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();
+ }
+}
--- /dev/null
+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;
+ }
+}
+++ /dev/null
-import java.util.Arrays;
-
-public aspect NegatedTypeAspect {
- before(): execution(!void get*()) {
- System.out.println("[GETTER] " + thisJoinPoint);
- }
-
- before(): execution(!String get*()) {
- System.out.println("[NON-STRING GETTER] " + thisJoinPoint);
- }
-
- before(): execution(String[] get*()) {
- System.out.println("[STRING-ARRAY GETTER] " + thisJoinPoint);
- }
-
- before(): execution(!String[] get*()) {
- System.out.println("[NON-STRING-ARRAY GETTER] " + thisJoinPoint);
- }
-
- before(): execution(!String[][] get*()) {
- System.out.println("[NON-STRING-ARRAY-ARRAY GETTER] " + thisJoinPoint);
- }
-
- before(): execution(void set*(*)) {
- System.out.println("[SETTER] " + thisJoinPoint);
- }
-
- public static void main(String[] args) {
- Person person = new Person();
- person.setId(11);
- person.setFirstName("Marie");
- person.setLastName("Curie");
- System.out.println(person);
- person.getId();
- person.getFirstName();
- person.getLastName();
- System.out.println(person.getFullName(false));
- person.setFullName("Albert Einstein");
- person.setId(22);
- System.out.println(person);
- System.out.println(person.getFullName(true));
- person.getVoid();
- System.out.println(Arrays.deepToString(person.getStringArray()));
- System.out.println(Arrays.deepToString(person.getStringArrayArray()));
- System.out.println(person.setSomething("something"));
- }
-}
-
-class Person {
- private int id;
- private String lastName;
- private String firstName;
-
- // Bean getters/setters, matched by aspect
-
- // Non-string getter, matched by corresponding pointcut
- public int getId() {
- return id;
- }
-
- public void setId(int id) {
- this.id = id;
- }
-
- public String getLastName() {
- return lastName;
- }
-
- public void setLastName(String lastName) {
- this.lastName = lastName;
- }
-
- public String getFirstName() {
- return firstName;
- }
-
- public void setFirstName(String firstName) {
- this.firstName = firstName;
- }
-
- // Non-string getter (String[] != String)
- public String[] getStringArray() {
- return new String[] {"Hello", "world"};
- }
-
- // Non-string, non-string-array getter (String[] != String, String[] != String[][])
- public String[][] getStringArrayArray() {
- return new String[][] {{"Hello", "world"}, {"Hallo", "Welt"}};
- }
-
- // Non-bean getters/setters, not matched by aspect
-
- public String getFullName(boolean lastNameFirst) {
- return lastNameFirst
- ? lastName + ", " + firstName
- : firstName + " " + lastName;
- }
-
- public void setFullName(String fullName) {
- boolean lastNameFirst = fullName.contains(",");
- String[] nameParts = fullName.split("[, ]+");
- if (lastNameFirst) {
- firstName = nameParts[1];
- lastName = nameParts[0];
- } else {
- firstName = nameParts[0];
- lastName = nameParts[1];
- }
- }
-
- public String setSomething(String something) {
- return "AspectJ rules!";
- }
-
- // Non-string getter, matched by corresponding pointcut
- public void getVoid() {}
-
- // Other methods, not matched by aspect
-
- @Override
- public String toString() {
- return "Person(" + "id=" + id + ", lastName='" + lastName + '\'' + ", firstName='" + firstName + '\'' + ')';
- }
-}
+++ /dev/null
-import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-public class JpaRepositoryDump implements Opcodes {
- public static void main(String[] args) throws IOException {
- // Write class file to test sandbox directory
- String classFile = args[0] + File.separator + "JpaRepository.class";
- try (FileOutputStream outputStream = new FileOutputStream(classFile)) {
- 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();
- }
-}
+++ /dev/null
-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;
- }
-}
/**
* 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>.
+ * See <a href="https://github.com/spring-projects/spring-framework/issues/27761">Spring GitHub issue 27761</a>,
+ * <a href="https://github.com/eclipse-aspectj/aspectj/issues/257">AspectJ GitHub issue 257</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>
</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">
+ <!--
+ https://github.com/spring-projects/spring-framework/issues/27761, AspectJ 1.9.20.1
+ https://github.com/eclipse-aspectj/aspectj/issues/257
+ -->
+ <ajc-test dir="bugs1920/github_spring_27761" vm="8" title="do not match bridge methods">
<!-- (1) Use ASM to generate JpaRepository class with reordered methods -->
<compile files="JpaRepositoryDump.java" options="-8"/>
<run class="JpaRepositoryDump" options="$sandbox"/>
</run>
</ajc-test>
- <!-- https://github.com/eclipse-aspectj/aspectj/issues/257 -->
- <ajc-test dir="bugs1921/gh_257" vm="8" title="handle negated type patterns correctly">
+ <!-- https://github.com/eclipse-aspectj/aspectj/issues/257, AspectJ 1.9.20.1 -->
+ <ajc-test dir="bugs1920/github_257" vm="8" title="handle negated type patterns correctly">
<compile files="NegatedTypeAspect.aj" options="-8"/>
<run class="NegatedTypeAspect">
<stdout>