8189778: Jshell crash on tab for StringBuilder.append(
authorjlahoda
Mon, 11 Dec 2017 18:33:53 +0100
changeset 48237 ee130cca69e6
parent 48236 31febb3f66f7
child 48238 9f225d4387e2
child 48283 da1b57b17101
8189778: Jshell crash on tab for StringBuilder.append( Summary: Fixing handling of {@inheritDoc} in JShell's documentation. Reviewed-by: jjg, ksrini
src/jdk.compiler/share/classes/jdk/internal/shellsupport/doc/JavadocHelper.java
test/langtools/jdk/internal/shellsupport/doc/JavadocHelperTest.java
--- a/src/jdk.compiler/share/classes/jdk/internal/shellsupport/doc/JavadocHelper.java	Fri Dec 08 16:28:14 2017 +0100
+++ b/src/jdk.compiler/share/classes/jdk/internal/shellsupport/doc/JavadocHelper.java	Mon Dec 11 18:33:53 2017 +0100
@@ -77,6 +77,7 @@
 import com.sun.source.tree.CompilationUnitTree;
 import com.sun.source.tree.MethodTree;
 import com.sun.source.tree.VariableTree;
+import com.sun.source.util.DocSourcePositions;
 import com.sun.source.util.DocTreePath;
 import com.sun.source.util.DocTreeScanner;
 import com.sun.source.util.DocTrees;
@@ -194,6 +195,8 @@
             String docComment = trees.getDocComment(el);
 
             if (docComment == null && element.getKind() == ElementKind.METHOD) {
+                //if a method does not have a javadoc,
+                //try to use javadoc from the methods overridden by this method:
                 ExecutableElement executableElement = (ExecutableElement) element;
                 Iterable<Element> superTypes =
                         () -> superTypeForInheritDoc(task, element.getEnclosingElement()).iterator();
@@ -215,27 +218,65 @@
                 }
             }
 
+            if (docComment == null)
+                return null;
+
             Pair<DocCommentTree, Integer> parsed = parseDocComment(task, docComment);
             DocCommentTree docCommentTree = parsed.fst;
             int offset = parsed.snd;
             IOException[] exception = new IOException[1];
-            Map<int[], String> replace = new TreeMap<>((span1, span2) -> span2[0] - span1[0]);
+            Comparator<int[]> spanComp =
+                    (span1, span2) -> span1[0] != span2[0] ? span2[0] - span1[0]
+                                                           : span2[1] - span1[0];
+            //spans in the docComment that should be replaced with the given Strings:
+            Map<int[], List<String>> replace = new TreeMap<>(spanComp);
+            DocSourcePositions sp = trees.getSourcePositions();
 
+            //fill in missing elements and resolve {@inheritDoc}
+            //if an element is (silently) missing in the javadoc, a synthetic {@inheritDoc}
+            //is created for it.
             new DocTreeScanner<Void, Void>() {
+                /* enclosing doctree that may contain {@inheritDoc} (explicit or synthetic)*/
                 private Stack<DocTree> interestingParent = new Stack<>();
+                /* current top-level DocCommentTree*/
                 private DocCommentTree dcTree;
-                private JavacTask inheritedJavacTask;
-                private TreePath inheritedTreePath;
+                /* javadoc from a super method from which we may copy elements.*/
                 private String inherited;
+                /* JavacTask from which inherited originates.*/
+                private JavacTask inheritedJavacTask;
+                /* TreePath to the super method from which inherited originates.*/
+                private TreePath inheritedTreePath;
+                /* Synthetic trees that contain {@inheritDoc} and
+                 * texts which which they should be replaced.*/
                 private Map<DocTree, String> syntheticTrees = new IdentityHashMap<>();
-                private long lastPos = 0;
+                /* Position on which the synthetic trees should be inserted.*/
+                private long insertPos = offset;
                 @Override @DefinedBy(Api.COMPILER_TREE)
                 public Void visitDocComment(DocCommentTree node, Void p) {
                     dcTree = node;
                     interestingParent.push(node);
                     try {
-                        scan(node.getFirstSentence(), p);
-                        scan(node.getBody(), p);
+                        if (node.getFullBody().isEmpty()) {
+                            //there is no body in the javadoc, add synthetic {@inheritDoc}, which
+                            //will be automatically filled in visitInheritDoc:
+                            DocCommentTree dc = parseDocComment(task, "{@inheritDoc}").fst;
+                            syntheticTrees.put(dc, "*\n");
+                            interestingParent.push(dc);
+                            boolean prevInSynthetic = inSynthetic;
+                            try {
+                                inSynthetic = true;
+                                scan(dc.getFirstSentence(), p);
+                                scan(dc.getBody(), p);
+                            } finally {
+                                inSynthetic = prevInSynthetic;
+                                interestingParent.pop();
+                            }
+                        } else {
+                            scan(node.getFirstSentence(), p);
+                            scan(node.getBody(), p);
+                        }
+                        //add missing @param, @throws and @return, augmented with {@inheritDoc}
+                        //which will be resolved in visitInheritDoc:
                         List<DocTree> augmentedBlockTags = new ArrayList<>(node.getBlockTags());
                         if (element.getKind() == ElementKind.METHOD) {
                             ExecutableElement executableElement = (ExecutableElement) element;
@@ -269,19 +310,19 @@
 
                             for (String missingParam : missingParams) {
                                 DocTree syntheticTag = parseBlockTag(task, "@param " + missingParam + " {@inheritDoc}");
-                                syntheticTrees.put(syntheticTag, "@param " + missingParam + " ");
+                                syntheticTrees.put(syntheticTag, "@param " + missingParam + " *\n");
                                 insertTag(augmentedBlockTags, syntheticTag, parameters, throwsList);
                             }
 
                             for (String missingThrow : missingThrows) {
                                 DocTree syntheticTag = parseBlockTag(task, "@throws " + missingThrow + " {@inheritDoc}");
-                                syntheticTrees.put(syntheticTag, "@throws " + missingThrow + " ");
+                                syntheticTrees.put(syntheticTag, "@throws " + missingThrow + " *\n");
                                 insertTag(augmentedBlockTags, syntheticTag, parameters, throwsList);
                             }
 
                             if (!hasReturn) {
                                 DocTree syntheticTag = parseBlockTag(task, "@return {@inheritDoc}");
-                                syntheticTrees.put(syntheticTag, "@return ");
+                                syntheticTrees.put(syntheticTag, "@return *\n");
                                 insertTag(augmentedBlockTags, syntheticTag, parameters, throwsList);
                             }
                         }
@@ -320,26 +361,32 @@
                 }
                 @Override @DefinedBy(Api.COMPILER_TREE)
                 public Void visitInheritDoc(InheritDocTree node, Void p) {
+                    //replace (schedule replacement into the replace map)
+                    //{@inheritDoc} with the corresponding text from an overridden method
+
+                    //first, fill in inherited, inheritedJavacTask and inheritedTreePath if not
+                    //done yet:
                     if (inherited == null) {
                         try {
                             if (element.getKind() == ElementKind.METHOD) {
                                 ExecutableElement executableElement = (ExecutableElement) element;
-                                Iterable<Element> superTypes = () -> superTypeForInheritDoc(task, element.getEnclosingElement()).iterator();
-                                OUTER: for (Element sup : superTypes) {
-                                   for (ExecutableElement supMethod : ElementFilter.methodsIn(sup.getEnclosedElements())) {
-                                       if (task.getElements().overrides(executableElement, supMethod, (TypeElement) executableElement.getEnclosingElement())) {
-                                           Pair<JavacTask, TreePath> source = getSourceElement(task, supMethod);
+                                Iterable<ExecutableElement> superMethods =
+                                        () -> superMethodsForInheritDoc(task, executableElement).
+                                              iterator();
+                                for (Element supMethod : superMethods) {
+                                   Pair<JavacTask, TreePath> source =
+                                           getSourceElement(task, supMethod);
 
-                                           if (source != null) {
-                                               String overriddenComment = getResolvedDocComment(source.fst, source.snd);
+                                   if (source != null) {
+                                       String overriddenComment =
+                                               getResolvedDocComment(source.fst,
+                                                                     source.snd);
 
-                                               if (overriddenComment != null) {
-                                                   inheritedJavacTask = source.fst;
-                                                   inheritedTreePath = source.snd;
-                                                   inherited = overriddenComment;
-                                                   break OUTER;
-                                               }
-                                           }
+                                       if (overriddenComment != null) {
+                                           inheritedJavacTask = source.fst;
+                                           inheritedTreePath = source.snd;
+                                           inherited = overriddenComment;
+                                           break;
                                        }
                                    }
                                 }
@@ -357,6 +404,8 @@
                     DocCommentTree inheritedDocTree = parsed.fst;
                     int offset = parsed.snd;
                     List<List<? extends DocTree>> inheritedText = new ArrayList<>();
+                    //find the corresponding piece in the inherited javadoc
+                    //(interesting parent keeps the enclosing tree):
                     DocTree parent = interestingParent.peek();
                     switch (parent.getKind()) {
                         case DOC_COMMENT:
@@ -401,18 +450,29 @@
                         long end = Long.MIN_VALUE;
 
                         for (DocTree t : inheritedText.get(0)) {
-                            start = Math.min(start, trees.getSourcePositions().getStartPosition(null, inheritedDocTree, t) - offset);
-                            end   = Math.max(end,   trees.getSourcePositions().getEndPosition(null, inheritedDocTree, t) - offset);
+                            start = Math.min(start,
+                                             sp.getStartPosition(null, inheritedDocTree, t) - offset);
+                            end   = Math.max(end,
+                                             sp.getEndPosition(null, inheritedDocTree, t) - offset);
                         }
-                        String text = inherited.substring((int) start, (int) end);
+                        String text = end >= 0 ? inherited.substring((int) start, (int) end) : "";
 
                         if (syntheticTrees.containsKey(parent)) {
-                            replace.put(new int[] {(int) lastPos + 1, (int) lastPos}, "\n" + syntheticTrees.get(parent) + text);
+                            //if the {@inheritDoc} is inside a synthetic tree, don't delete anything,
+                            //but insert the required text
+                            //(insertPos is the position at which new stuff should be added):
+                            int[] span = new int[] {(int) insertPos, (int) insertPos};
+                            replace.computeIfAbsent(span, s -> new ArrayList<>())
+                                    .add(syntheticTrees.get(parent).replace("*", text));
                         } else {
-                            long inheritedStart = trees.getSourcePositions().getStartPosition(null, dcTree, node);
-                            long inheritedEnd   = trees.getSourcePositions().getEndPosition(null, dcTree, node);
+                            //replace the {@inheritDoc} with the full text from
+                            //the overridden method:
+                            long inheritedStart = sp.getStartPosition(null, dcTree, node);
+                            long inheritedEnd   = sp.getEndPosition(null, dcTree, node);
+                            int[] span = new int[] {(int) inheritedStart, (int) inheritedEnd};
 
-                            replace.put(new int[] {(int) inheritedStart, (int) inheritedEnd}, text);
+                            replace.computeIfAbsent(span, s -> new ArrayList<>())
+                                    .add(text);
                         }
                     }
                     return super.visitInheritDoc(node, p);
@@ -428,13 +488,31 @@
                         inSynthetic |= syntheticTrees.containsKey(tree);
                         return super.scan(tree, p);
                     } finally {
-                        if (!inSynthetic) {
-                            lastPos = trees.getSourcePositions().getEndPosition(null, dcTree, tree);
+                        if (!inSynthetic && tree != null) {
+                            //for nonsynthetic trees, preserve the ending position as the future
+                            //insertPos (as future missing elements should be inserted behind
+                            //this tree)
+                            //if there is a newline immediately behind this tree, insert behind
+                            //the newline:
+                            long endPos = sp.getEndPosition(null, dcTree, tree);
+                            if (endPos >= 0) {
+                                if (endPos - offset + 1 < docComment.length() &&
+                                    docComment.charAt((int) (endPos - offset + 1)) == '\n') {
+                                    endPos++;
+                                }
+                                if (endPos - offset < docComment.length()) {
+                                    insertPos = endPos + 1;
+                                } else {
+                                    insertPos = endPos;
+                                }
+                            }
                         }
                         inSynthetic = prevInSynthetic;
                     }
                 }
 
+                /* Insert a synthetic tag (toInsert) into the list of tags at
+                 * an appropriate position.*/
                 private void insertTag(List<DocTree> tags, DocTree toInsert, List<String> parameters, List<String> throwsTypes) {
                     Comparator<DocTree> comp = (tag1, tag2) -> {
                         if (tag1.getKind() == tag2.getKind()) {
@@ -479,16 +557,30 @@
             if (replace.isEmpty())
                 return docComment;
 
+            //do actually replace {@inheritDoc} with the new text (as scheduled by the visitor
+            //above):
             StringBuilder replacedInheritDoc = new StringBuilder(docComment);
 
-            for (Entry<int[], String> e : replace.entrySet()) {
-                replacedInheritDoc.delete(e.getKey()[0] - offset, e.getKey()[1] - offset + 1);
-                replacedInheritDoc.insert(e.getKey()[0] - offset, e.getValue());
+            for (Entry<int[], List<String>> e : replace.entrySet()) {
+                replacedInheritDoc.delete(e.getKey()[0] - offset, e.getKey()[1] - offset);
+                replacedInheritDoc.insert(e.getKey()[0] - offset,
+                                          e.getValue().stream().collect(Collectors.joining("")));
             }
 
             return replacedInheritDoc.toString();
         }
 
+        /* Find methods from which the given method may inherit javadoc, in the proper order.*/
+        private Stream<ExecutableElement> superMethodsForInheritDoc(JavacTask task,
+                                                                     ExecutableElement method) {
+            TypeElement type = (TypeElement) method.getEnclosingElement();
+
+            return this.superTypeForInheritDoc(task, type)
+                       .flatMap(sup -> ElementFilter.methodsIn(sup.getEnclosedElements()).stream())
+                       .filter(supMethod -> task.getElements().overrides(method, supMethod, type));
+        }
+
+        /* Find types from which methods in type may inherit javadoc, in the proper order.*/
         private Stream<Element> superTypeForInheritDoc(JavacTask task, Element type) {
             TypeElement clazz = (TypeElement) type;
             Stream<Element> result = interfaces(clazz);
@@ -529,7 +621,7 @@
                 };
                 DocCommentTree tree = trees.getDocCommentTree(fo);
                 int offset = (int) trees.getSourcePositions().getStartPosition(null, tree, tree);
-                offset += "<body>".length() + 1;
+                offset += "<body>".length();
                 return Pair.of(tree, offset);
             } catch (URISyntaxException ex) {
                 throw new IllegalStateException(ex);
--- a/test/langtools/jdk/internal/shellsupport/doc/JavadocHelperTest.java	Fri Dec 08 16:28:14 2017 +0100
+++ b/test/langtools/jdk/internal/shellsupport/doc/JavadocHelperTest.java	Mon Dec 11 18:33:53 2017 +0100
@@ -23,28 +23,37 @@
 
 /*
  * @test
- * @bug 8131019 8190552
+ * @bug 8131019 8189778 8190552
  * @summary Test JavadocHelper
  * @library /tools/lib
  * @modules jdk.compiler/com.sun.tools.javac.api
  *          jdk.compiler/com.sun.tools.javac.main
  *          jdk.compiler/jdk.internal.shellsupport.doc
  * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask
- * @run testng JavadocHelperTest
+ * @run testng/timeout=900/othervm JavadocHelperTest
  */
 
 import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import java.util.function.Function;
 import java.util.jar.JarEntry;
 import java.util.jar.JarOutputStream;
 
 import javax.lang.model.element.Element;
+import javax.lang.model.element.ModuleElement;
+import javax.lang.model.element.ModuleElement.ExportsDirective;
+import javax.lang.model.element.TypeElement;
 import javax.lang.model.util.ElementFilter;
 import javax.tools.Diagnostic.Kind;
 import javax.tools.DiagnosticListener;
@@ -70,15 +79,15 @@
                       "Top level. ");
         doTestJavadoc("",
                       t -> getFirstMethod(t, "test.Super"),
-                      " javadoc1A\n" +
+                      " javadoc1\n" +
                       "\n" +
-                      " @param p1 param1A\n" +
-                      " @param p2 param2A\n" +
-                      " @param p3 param3A\n" +
-                      " @throws IllegalStateException exc1A\n" +
-                      " @throws IllegalArgumentException exc2A\n" +
-                      " @throws IllegalAccessException exc3A\n" +
-                      " @return valueA\n");
+                      " @param p1 param1\n" +
+                      " @param p2 param2\n" +
+                      " @param p3 param3\n" +
+                      " @throws IllegalStateException exc1\n" +
+                      " @throws IllegalArgumentException exc2\n" +
+                      " @throws IllegalAccessException exc3\n" +
+                      " @return value\n");
     }
 
     private Element getFirstMethod(JavacTask task, String typeName) {
@@ -90,15 +99,15 @@
     public void testInheritNoJavadoc() throws Exception {
         doTestJavadoc("",
                       getSubTest,
-                      " javadoc1A\n" +
+                      " javadoc1\n" +
                       "\n" +
-                      " @param p1 param1A\n" +
-                      " @param p2 param2A\n" +
-                      " @param p3 param3A\n" +
-                      " @throws IllegalStateException exc1A\n" +
-                      " @throws IllegalArgumentException exc2A\n" +
-                      " @throws IllegalAccessException exc3A\n" +
-                      " @return valueA\n");
+                      " @param p1 param1\n" +
+                      " @param p2 param2\n" +
+                      " @param p3 param3\n" +
+                      " @throws IllegalStateException exc1\n" +
+                      " @throws IllegalArgumentException exc2\n" +
+                      " @throws IllegalAccessException exc3\n" +
+                      " @return value\n");
     }
 
     public void testInheritFull() throws Exception {
@@ -140,7 +149,7 @@
                       " Prefix javadoc1 suffix.\n" +
                       "\n" +
                       " @param p1 prefix param1 suffix\n" +
-                      "@param p2  param2\n" +
+                      "@param p2 param2\n" +
                       " @param p3 prefix param3 suffix\n" +
                       " @throws IllegalStateException prefix exc1 suffix\n" +
                       " @throws IllegalArgumentException prefix exc2 suffix\n" +
@@ -161,8 +170,8 @@
                       "     */\n",
                       getSubTest,
                       " Prefix javadoc1 suffix.\n" +
-                      "@param p1  param1\n" +
                       "\n" +
+                      "@param p1 param1\n" +
                       " @param p2 prefix param2 suffix\n" +
                       " @param p3 prefix param3 suffix\n" +
                       " @throws IllegalStateException prefix exc1 suffix\n" +
@@ -189,7 +198,7 @@
                       " @param p2 prefix param2 suffix\n" +
                       " @param p3 prefix param3 suffix\n" +
                       " @throws IllegalStateException prefix exc1 suffix\n" +
-                      "@throws java.lang.IllegalArgumentException  exc2\n" +
+                      "@throws java.lang.IllegalArgumentException exc2\n" +
                       " @throws IllegalAccessException prefix exc3 suffix\n" +
                       " @return prefix value suffix\n");
     }
@@ -214,11 +223,101 @@
                       " @throws IllegalStateException prefix exc1 suffix\n" +
                       " @throws IllegalArgumentException prefix exc2 suffix\n" +
                       " @throws IllegalAccessException prefix exc3 suffix\n" +
-                      "@return  value\n");
+                      "@return value\n");
+    }
+
+    public void testInheritAllButOne() throws Exception {
+        doTestJavadoc("    /**\n" +
+                      "     * @throws IllegalArgumentException {@inheritDoc}\n" +
+                      "     */\n",
+                      getSubTest,
+                      "javadoc1\n" +
+                      "@param p1 param1\n" +
+                      "@param p2 param2\n" +
+                      "@param p3 param3\n" +
+                      "@throws java.lang.IllegalStateException exc1\n" +
+                      " @throws IllegalArgumentException exc2\n" +
+                      "@throws java.lang.IllegalAccessException exc3\n" +
+                      "@return value\n");
+    }
+
+    public void testInheritEmpty() throws Exception {
+        doTestJavadoc("    /**\n" +
+                      "     */\n",
+                      "    /**@param p1\n" +
+                      "     * @param p2\n" +
+                      "     * @param p3\n" +
+                      "     * @throws IllegalStateException\n" +
+                      "     * @throws IllegalArgumentException\n" +
+                      "     * @throws IllegalAccessException\n" +
+                      "     * @return\n" +
+                      "     */\n",
+                      getSubTest,
+                      "\n" +
+                      "@param p1 \n" +
+                      "@param p2 \n" +
+                      "@param p3 \n" +
+                      "@throws java.lang.IllegalStateException \n" +
+                      "@throws java.lang.IllegalArgumentException \n" +
+                      "@throws java.lang.IllegalAccessException \n" +
+                      "@return \n");
     }
 
+    public void testEmptyValue() throws Exception {
+        doTestJavadoc("    /**\n" +
+                      "     */\n",
+                      "    /**@param p1 {@value}\n" +
+                      "     * @param p2\n" +
+                      "     * @param p3\n" +
+                      "     * @throws IllegalStateException\n" +
+                      "     * @throws IllegalArgumentException\n" +
+                      "     * @throws IllegalAccessException\n" +
+                      "     * @return\n" +
+                      "     */\n",
+                      getSubTest,
+                      "\n" +
+                      "@param p1 {@value}\n" +
+                      "@param p2 \n" +
+                      "@param p3 \n" +
+                      "@throws java.lang.IllegalStateException \n" +
+                      "@throws java.lang.IllegalArgumentException \n" +
+                      "@throws java.lang.IllegalAccessException \n" +
+                      "@return \n");
+    }
+
+    public void testShortComment() throws Exception {
+        doTestJavadoc("    /**Test.*/\n",
+                      getSubTest,
+                      "Test." +
+                      "@param p1 param1\n" +
+                      "@param p2 param2\n" +
+                      "@param p3 param3\n" +
+                      "@throws java.lang.IllegalStateException exc1\n" +
+                      "@throws java.lang.IllegalArgumentException exc2\n" +
+                      "@throws java.lang.IllegalAccessException exc3\n" +
+                      "@return value\n");
+    }
 
     private void doTestJavadoc(String origJavadoc, Function<JavacTask, Element> getElement, String expectedJavadoc) throws Exception {
+        doTestJavadoc(origJavadoc,
+                      "    /**\n" +
+                      "     * javadoc1\n" +
+                      "     *\n" +
+                      "     * @param p1 param1\n" +
+                      "     * @param p2 param2\n" +
+                      "     * @param p3 param3\n" +
+                      "     * @throws IllegalStateException exc1\n" +
+                      "     * @throws IllegalArgumentException exc2\n" +
+                      "     * @throws IllegalAccessException exc3\n" +
+                      "     * @return value\n" +
+                      "     */\n",
+                      getElement, expectedJavadoc);
+    }
+
+    private void doTestJavadoc(String origJavadoc,
+                               String superJavadoc,
+                               Function<JavacTask, Element> getElement,
+                               String expectedJavadoc) throws Exception {
         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
         String subClass =
                 "package test;\n" +
@@ -231,17 +330,7 @@
                 "/**Top level." +
                 " */\n" +
                 "public class Super {\n" +
-                "    /**\n" +
-                "     * javadoc1A\n" +
-                "     *\n" +
-                "     * @param p1 param1A\n" +
-                "     * @param p2 param2A\n" +
-                "     * @param p3 param3A\n" +
-                "     * @throws IllegalStateException exc1A\n" +
-                "     * @throws IllegalArgumentException exc2A\n" +
-                "     * @throws IllegalAccessException exc3A\n" +
-                "     * @return valueA\n" +
-                "     */\n" +
+                superJavadoc +
                 "    public String test(int p1, int p2, int p3) throws IllegalStateException, IllegalArgumentException, IllegalAccessException { return null;} \n" +
                 "}\n";
 
@@ -293,4 +382,53 @@
         }
 
     }
+
+    public void testAllDocs() throws IOException {
+        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+        DiagnosticListener<? super JavaFileObject> noErrors = d -> {
+            if (d.getKind() == Kind.ERROR) {
+                throw new AssertionError(d.getMessage(null));
+            }
+        };
+
+        List<Path> sources = new ArrayList<>();
+        Path home = Paths.get(System.getProperty("java.home"));
+        Path srcZip = home.resolve("lib").resolve("src.zip");
+        if (Files.isReadable(srcZip)) {
+            URI uri = URI.create("jar:" + srcZip.toUri());
+            try (FileSystem zipFO = FileSystems.newFileSystem(uri, Collections.emptyMap())) {
+                Path root = zipFO.getRootDirectories().iterator().next();
+
+                //modular format:
+                try (DirectoryStream<Path> ds = Files.newDirectoryStream(root)) {
+                    for (Path p : ds) {
+                        if (Files.isDirectory(p)) {
+                            sources.add(p);
+                        }
+                    }
+                }
+                try (StandardJavaFileManager fm =
+                        compiler.getStandardFileManager(null, null, null)) {
+                    JavacTask task =
+                            (JavacTask) compiler.getTask(null, fm, noErrors, null, null, null);
+                    task.getElements().getTypeElement("java.lang.Object");
+                    for (ModuleElement me : task.getElements().getAllModuleElements()) {
+                        List<ExportsDirective> exports =
+                                ElementFilter.exportsIn(me.getDirectives());
+                        for (ExportsDirective ed : exports) {
+                            try (JavadocHelper helper = JavadocHelper.create(task, sources)) {
+                                List<? extends Element> content =
+                                        ed.getPackage().getEnclosedElements();
+                                for (TypeElement clazz : ElementFilter.typesIn(content)) {
+                                    for (Element el : clazz.getEnclosedElements()) {
+                                        helper.getResolvedDocComment(el);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
 }