Hi Jeff,

Thanks for the feedback that is helpful.

jdeps is static analysis tool reporting class dependencies to help existing code for modularization. So jdeps should report the list of class names matching the list that are needed during compilation as closely as possible. It looks at Signature, RuntimeVisibleAnnotations and RuntimeVisibleParameterAnnotations attributes but not other attributes in the current implementation that explains some of the questions you pointed.

My comments are inlined below from my initial evaluation. I am including your report in JDK-8134625 to be addressed together.

On 09/01/2015 11:59 AM, Jeff Hain wrote:

Back from trying to figure out all the cases where
jdeps and my utility don't compute the same dependencies,
for classes of JDK8_60/rt.jar, and each time which
is right.


What I did is modify my code to make it behave like
jdeps for each encountered difference, and then
re-run the comparison to see what was left.


Below I list all the cases for usage of jdeps
with "-verbose:class -filter:none" options,
i.e. when computing all dependencies, API or not
(with "-apionly" option there were many more
differences, possibly due to assumptions about
what "API" means - we could exclude that for now).



1) Jdeps ignores dependencies caused by package-info
class files.
===> Maybe intended?

Ex.:
@javax.xml.bind.annotation.XmlSchema(namespace = "http://xmlns.oracle.com/webservices/jaxws-databinding";, elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.oracle.xmlns.internal.webservices.jaxws_databinding;
===> Doesn't see dependencies to
java.lang.Object
and
javax.xml.bind.annotation.XmlSchema


javax.xml.bind.annotation.XmlSchema is needed to compile package-info.java and so I think it should be reported.


2) Jdeps ignores types of parameters of annotations
(as said in initial mail).

Ex. (in java.beans):
@Documented @Target(CONSTRUCTOR) @Retention(RUNTIME)
public @interface ConstructorProperties {
    String[] value();
}
===> Doesn't see dependencies to
java.lang.annotation.ElementType
and
java.lang.annotation.RetentionPolicy.

===> Could reproduce the behavior by ignoring/skipping
"element_value" items.


Enum and Class element value should be reported.



3) Jdeps doesn't parse class signatures containing generic types
properly, but it does fine for fields or methods signatures.

Ex. (in java.lang.management.PlatformComponent):
interface MXBeanFetcher<T extends PlatformManagedObject> {
    public List<T> getMXBeans();
}
===> Doesn't see dependency to
java.lang.management.PlatformManagedObject.

===> For example, dependency to Object is properly computed from signature
     "Ljava/lang/ThreadLocal<Ljava/lang/Object;>;"
     but not from
"<E:Ljava/lang/Object;>Ljava/lang/ThreadLocal<TE;>;".

===> Could reproduce the behavior by skipping all characters following ':'
     up to the '>' closing the scope.


I would think it should report it.  I will have to look at this one.


4) Jdeps doesn't compute a dependency to java.lang.Deprecated when
@deprecated or @Deprecated is only in the Javadoc,
in which case there is no "Ljava/lang/Deprecated;" descriptor/signature
in the class file but still a "Deprecated" attribute.
===> Maybe intended?

Ex.: javax.xml.bind.Validator

===> Could reproduce the behavior by skipping "Deprecated" attributes,
and only having dependency to java.lang.Deprecated due to the descriptor/signature.


I think we should skip Deprecated attribute since this is for documentation although a source may need java.lang.Deprecated type to compile (@Deprecated on a method e.g.)


5) Jdeps ignores (some? all?) dependencies to
com.sun.istack.internal.NotNull
and
com.sun.istack.internal.Nullable

Ex. (in com.sun.istack.internal.Pool):
public interface Pool<T> {
    @NotNull T take();
    void recycle(@NotNull T t);
}
===> Doesn't see dependencies to
com.sun.istack.internal.NotNull

===> Could not find a way to reproduce this behavior, other than
having specific cases for these annotations.
I created a similar test class, with a similar annotation @MyNotNull,
and jdeps would detect it (???).



I think it should but I'll check this out in details.

6) For java.util.stream.FindOps, jdeps properly doesn't compute
a dependency to java.util.stream.TerminalSink (which is only
used in nested classes), but for some reason there is a
"()Ljava/util/stream/TerminalSink;" descriptor in FindOps.class
(???) so my library considers there is the dependency...



Lastly a few remarks about -apionly:

jdeps help says it restricts the analysis to dependences
"from the signature of public and protected members of public
classes including field type, method parameter types,
returned type, checked exception types etc"


-apionly is useful to separate dependencies due to implementation vs API.

But:

1) It can actually compute dependencies from a protected,
   package-private or private class, for example if they
   have public fields.
   Maybe it should not be the case for (package-)private
   classes (or whenever there is a non-API class in the
   way to the top level class), because it would show
   transitive dependencies that could not be followed
   through the API.
   (Ex.:
    private class Priv {public Double getDouble(){...}}
    public class Pub {public Priv getPriv(){...}}
    ===> Through the API we could never go from Pub to Double
         but according to jdeps it would be possible.)


This is a bug. It should inspect inner classes only if the outer class is public.

2) It does not consider public and protected nested classes
   as dependencies (even though they are in the API
   of the class).

3) Should annotations of APIs be considered APIs?
   (at least for some trivial cases they are)

The purpose of -apionly is to find out API dependencies to catch any undesirable types used in the signatures unexpectedly. Annotations are not part of the signatures and they are not reported.

Thanks again for the feedback.

Mandy

Reply via email to