How to detect / resolve super method invocations with jqassistant

146 Views Asked by At

I am using jqassistant 1.8.0 to detect super method calls in a class hierarchy. It seems some method invocations within a class hierarchy are missing, even after applying the classpath:Resolve concept. Minimal set-up consist of three classes:

public class SuperClass {

  void superMethod() {
  }

}

public class SubClass1 extends SuperClass {

  void subMethod1() {
    super.superMethod();
  }

}

public class SubClass2 extends SuperClass {

  void subMethod2() {
    superMethod();
  }
}

Both subMethod1 and subMethod2 are calling the same method of SuperClass, but only the one with the explicit "super." invocation has the :INVOKES relationship.

MATCH
  (who)-[:INVOKES]->(m)<-[:DECLARES]-(:Type {fqn: 'SuperClass'})
Return who

In the database two nodes with a signature "void superMethod()" exist, one declared by the SuperClass and one declared by SubClass2. It seems there is some step missing that links the two identical methods.

Is there another built-in concept (apart from classpath:Resolve) resolving this or is this not covered by the java-plugin? Thanks!

1

There are 1 best solutions below

3
Dirk Mahler On BEST ANSWER

There's a slight difference between calling super.superMethod() and superMethod():

The first instructs the JVM to use the method from the super class, the second relies on resolving the method at runtime, there might be an implementation in SubClass2 (virtual invocation):

For this case the created graph contains an invocation to a method placeholder in SubClass2 (only having the signature property):

(method)-[:INVOKES]->(:Method{signature:"void superMethod()"})<-[:DECLARES]-(:Type{name:"SubClass2"}) 

There's a concept java:MethodOverrides that should create an OVERRIDES relation to the super class method but sadly it does not work in this case (up to jQA 1.8.0). There's already a fix applied which will come with jQA 1.9.0:

            MATCH
              (type:Type)-[:DECLARES]->(method:Method),
              (superType:Type)-[:DECLARES]->(superMethod:Method),
              path=shortestPath((type)-[:EXTENDS|IMPLEMENTS*]->(superType))
            WHERE
              method.signature = superMethod.signature
              and superMethod.visibility <> "private"
            WITH
              type, method, superType, superMethod, length(path) as depth
            ORDER BY
              depth asc
            WITH
              method, head(collect(superMethod)) as overriddenMethod
            MERGE
              (method)-[:OVERRIDES]->(overriddenMethod)
            RETURN
              count(*) as OverriddenMethods

Using this one you should be able to exeute the following query:

MATCH
  (:Type{name:"SuperClass"})-[:DECLARES]->(m:Method{name:"superMethod"}),
  (who)-[:INVOKES]->(:Method)-[:OVERRIDES*0..1]->(m) // catching direct or virtual invocations
RETURN
  who