Bug 548565 - hasfield() not working in @DeclareParents context
Summary: hasfield() not working in @DeclareParents context
Status: NEW
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: 1.9.2   Edit
Hardware: PC Windows 10
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: aspectj inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-06-24 02:06 EDT by Alexander Kriegisch CLA
Modified: 2019-06-27 23:58 EDT (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Alexander Kriegisch CLA 2019-06-24 02:06:36 EDT
Let's assume I want to intercept method calls whenever a method parameter type contains a field with a certain annotation:

    package de.scrum_master.app;
    
    import static java.lang.annotation.ElementType.FIELD;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    
    @Retention(RUNTIME)
    @Target(FIELD)
    public @interface MyAnnotation {}

------------------------------------------------------------

    package de.scrum_master.app;
    
    public class MyClass {
      private int id;
      @MyAnnotation
      private String name;
    
      public MyClass(int id, String name) {
        this.id = id;
        this.name = name;
      }
    
      @Override
      public String toString() {
        return "MyClass [id=" + id + ", name=" + name + "]";
      }
    }

------------------------------------------------------------

    package de.scrum_master.app;
    
    public class Application {
      public void doSomething() {}
      public void doSomethingElse(int i, String string) {}
      public void doSomethingSpecial(int i, MyClass myClass) {}
      public int doSomethingVerySpecial(MyClass myClass) { return 0; }
    
      public static void main(String[] args) {
        Application application = new Application();
        application.doSomething();
        application.doSomethingElse(7, "foo");
        application.doSomethingSpecial(3, new MyClass(11, "John Doe"));
        application.doSomethingVerySpecial(new MyClass(22, "Jane Doe"));
      }
    }

Now in native syntax we could just make each target class implement a marker interface and then intercept method calls with parameters of that interface type:


    package de.scrum_master.app;
    
    public interface HasMyAnnotationField {}

------------------------------------------------------------

    package de.scrum_master.aspect;
    
    import de.scrum_master.app.HasMyAnnotationField;
    import de.scrum_master.app.MyAnnotation;
    
    public aspect MyNativeSyntaxAspect {
      declare parents : hasfield(@MyAnnotation * *) implements HasMyAnnotationField;
    
      before() : execution(* *(.., HasMyAnnotationField+, ..)) {
        System.out.println(thisJoinPoint);
      }
    }

When compiled with -XhasMember, this would print:

    execution(void de.scrum_master.app.Application.doSomethingSpecial(int, MyClass))
    execution(int de.scrum_master.app.Application.doSomethingVerySpecial(MyClass))

Now if I am trying to to the same with @AspectJ syntax:

    package de.scrum_master.aspect;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.DeclareParents;
    
    import de.scrum_master.app.HasMyAnnotationField;
    
    @Aspect
    public class MyAnnotationSyntaxAspect {
      @DeclareParents("hasfield(@de.scrum_master.app.MyAnnotation * *)")
      private HasMyAnnotationField hasMyAnnotationField;
    
      @Before("execution(* *(.., de.scrum_master.app.HasMyAnnotationField+, ..))")
      public void myAdvice(JoinPoint thisJoinPoint) {
        System.out.println(thisJoinPoint);
      }
    }

It compiled nicely but @DeclareParents has no effect according to 'javap -p'.

It gets even weirder when I change the aspect like this:

  @DeclareParents("de.scrum_master.app.MyClass")
  private HasMyAnnotationField hasMyAnnotationField;

Now 'javap -p' does show that MyClass implements the interface, but still the advice does not get triggered.

I also tried to add a default implementation to the interface, no dice. I even tried the alternative approach with @DeclareMixin, which I am not very familiar with, again without any effect. But maybe I made a mistake there.

I am not an Ajc expert and know there are limitations concerning what can be expressed in @AspectJ style. But actually I see no obvious reason why this should not work.
Comment 1 Alexander Kriegisch CLA 2019-06-27 23:58:04 EDT
BTW, this is where my question initially came from:

https://stackoverflow.com/a/56788724/1082681