1

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 Answers1

1

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
Dirk Mahler
  • 1,186
  • 1
  • 6
  • 7
  • Thanks! I had a temporary solution with a `MERGE` that inserted direct :INVOKES relationship between SuperClass Method and the invoker (similar to the RESOLVES_TO handling in the classpath:Resolve concept) to also get a match with plain :INVOKES relation. This scenario is not covered by your approach, right? But I get the JVM reasoning. – christian-de Mar 30 '20 at 11:09
  • There's another concept ```VirtualInvokes``` which promotes ```INVOKES``` relations to implementing/overriding sub-classes, it seems to me that this should cover your case as well, let's see if this can be done. – Dirk Mahler Mar 30 '20 at 16:03
  • Created an issue to provide a more fundamental solution, see https://github.com/jQAssistant/jqa-java-plugin/issues/25. – Dirk Mahler Apr 13 '20 at 10:37