8153042: jdeps should continue to report JDK internal APIs that are removed/renamed in JDK
authormchung
Fri, 20 May 2016 12:24:02 -0700
changeset 38532 24f77d64bb1f
parent 38531 c449daa25b45
child 38533 2bd347dde526
8153042: jdeps should continue to report JDK internal APIs that are removed/renamed in JDK Reviewed-by: dfuchs
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Analyzer.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DepsAnalyzer.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleInfoBuilder.java
langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties
langtools/test/tools/jdeps/jdkinternals/RemovedJDKInternals.java
langtools/test/tools/jdeps/jdkinternals/patches/java.desktop/com/sun/image/codec/jpeg/JPEGCodec.java
langtools/test/tools/jdeps/jdkinternals/patches/jdk.unsupported/sun/misc/Service.java
langtools/test/tools/jdeps/jdkinternals/patches/jdk.unsupported/sun/misc/SoftCache.java
langtools/test/tools/jdeps/jdkinternals/src/p/Main.java
langtools/test/tools/jdeps/jdkinternals/src/p/S.java
langtools/test/tools/jdeps/lib/ModuleMetaData.java
langtools/test/tools/jdeps/modules/ModuleMetaData.java
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Analyzer.java	Fri May 20 11:55:46 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Analyzer.java	Fri May 20 12:24:02 2016 -0700
@@ -369,9 +369,9 @@
         }
     }
 
-    private static final JdkInternals REMOVED_JDK_INTERNALS = new JdkInternals();
+    static final JdkInternals REMOVED_JDK_INTERNALS = new JdkInternals();
 
-    private static class JdkInternals extends Module {
+    static class JdkInternals extends Module {
         private final String BUNDLE = "com.sun.tools.jdeps.resources.jdkinternals";
 
         private final Set<String> jdkinternals;
@@ -406,6 +406,11 @@
         }
 
         @Override
+        public boolean isJDK() {
+            return true;
+        }
+
+        @Override
         public boolean isExported(String pn) {
             return false;
         }
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DepsAnalyzer.java	Fri May 20 11:55:46 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DepsAnalyzer.java	Fri May 20 12:24:02 2016 -0700
@@ -266,7 +266,8 @@
         MODULE_PRIVATE,
         QUALIFIED_EXPORTED_API,
         INTERNAL_API,
-        JDK_INTERNAL_API
+        JDK_INTERNAL_API,
+        JDK_REMOVED_INTERNAL_API
     }
 
     public static class Node {
@@ -372,8 +373,9 @@
             info = Info.EXPORTED_API;
         } else {
             Module module = target.getModule();
-
-            if (!source.getModule().isJDK() && module.isJDK())
+            if (module == Analyzer.REMOVED_JDK_INTERNALS) {
+                info = Info.JDK_REMOVED_INTERNAL_API;
+            } else if (!source.getModule().isJDK() && module.isJDK())
                 info = Info.JDK_INTERNAL_API;
                 // qualified exports or inaccessible
             else if (module.isExported(pn, source.getModule().name()))
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java	Fri May 20 11:55:46 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java	Fri May 20 12:24:02 2016 -0700
@@ -26,6 +26,7 @@
 package com.sun.tools.jdeps;
 
 import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
+import static com.sun.tools.jdeps.Analyzer.REMOVED_JDK_INTERNALS;
 import static com.sun.tools.jdeps.Analyzer.Type.*;
 import static com.sun.tools.jdeps.JdepsWriter.*;
 import static com.sun.tools.jdeps.JdepsConfiguration.ALL_MODULE_PATH;
@@ -666,19 +667,17 @@
         });
 
         if (!ok && !options.nowarning) {
-            log.println("Missing dependencies");
+            log.println("ERROR: missing dependencies");
             builder.visitMissingDeps(
                 new Analyzer.Visitor() {
                     @Override
                     public void visitDependence(String origin, Archive originArchive,
                                                 String target, Archive targetArchive) {
-                        if (targetArchive == NOT_FOUND)
+                        if (builder.notFound(targetArchive))
                             log.format("   %-50s -> %-50s %s%n",
                                 origin, target, targetArchive.getName());
                     }
                 });
-
-            log.println("ERROR: missing dependencies (check \"requires NOT_FOUND;\")");
         }
         return ok;
     }
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleInfoBuilder.java	Fri May 20 11:55:46 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleInfoBuilder.java	Fri May 20 12:24:02 2016 -0700
@@ -25,7 +25,7 @@
 package com.sun.tools.jdeps;
 
 import static com.sun.tools.jdeps.JdepsTask.*;
-import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
+import static com.sun.tools.jdeps.Analyzer.*;
 import static com.sun.tools.jdeps.JdepsFilter.DEFAULT_FILTER;
 
 import java.io.IOException;
@@ -39,6 +39,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
@@ -56,7 +57,10 @@
 
     final DependencyFinder dependencyFinder;
     final Analyzer analyzer;
-    final Map<Module, Module> strictModules;
+
+    // an input JAR file (loaded as an automatic module for analysis)
+    // maps to an explicit module to generate module-info.java
+    final Map<Module, Module> automaticToExplicitModule;
     public ModuleInfoBuilder(JdepsConfiguration configuration,
                              List<String> args,
                              Path outputdir) {
@@ -64,27 +68,27 @@
         this.outputdir = outputdir;
 
         this.dependencyFinder = new DependencyFinder(configuration, DEFAULT_FILTER);
-        this.analyzer = new Analyzer(configuration, Analyzer.Type.CLASS, DEFAULT_FILTER);
+        this.analyzer = new Analyzer(configuration, Type.CLASS, DEFAULT_FILTER);
 
         // add targets to modulepath if it has module-info.class
         List<Path> paths = args.stream()
             .map(fn -> Paths.get(fn))
             .collect(Collectors.toList());
 
-        // automatic module to convert to strict module
-        this.strictModules = ModuleFinder.of(paths.toArray(new Path[0]))
+        // automatic module to convert to explicit module
+        this.automaticToExplicitModule = ModuleFinder.of(paths.toArray(new Path[0]))
                 .findAll().stream()
                 .map(configuration::toModule)
                 .collect(Collectors.toMap(Function.identity(), Function.identity()));
 
-        Optional<Module> om = strictModules.keySet().stream()
+        Optional<Module> om = automaticToExplicitModule.keySet().stream()
                                     .filter(m -> !m.descriptor().isAutomatic())
                                     .findAny();
         if (om.isPresent()) {
             throw new UncheckedBadArgs(new BadArgs("err.genmoduleinfo.not.jarfile",
                                                    om.get().getPathName()));
         }
-        if (strictModules.isEmpty()) {
+        if (automaticToExplicitModule.isEmpty()) {
             throw new UncheckedBadArgs(new BadArgs("err.invalid.path", args));
         }
     }
@@ -99,68 +103,90 @@
 
             analyzer.run(automaticModules(), dependencyFinder.locationToArchive());
 
-            // computes requires and requires public
-            automaticModules().forEach(m -> {
-                Map<String, Boolean> requires;
-                if (requiresPublic.containsKey(m)) {
-                    requires = requiresPublic.get(m).stream()
-                        .map(Archive::getModule)
-                        .collect(Collectors.toMap(Module::name, (v) -> Boolean.TRUE));
+            boolean missingDeps = false;
+            for (Module m : automaticModules()) {
+                Set<Archive> apiDeps = requiresPublic.containsKey(m)
+                                            ? requiresPublic.get(m)
+                                            : Collections.emptySet();
+
+                Path file = outputdir.resolve(m.name()).resolve("module-info.java");
+
+                // computes requires and requires public
+                Module explicitModule = toExplicitModule(m, apiDeps);
+                if (explicitModule != null) {
+                    automaticToExplicitModule.put(m, explicitModule);
+
+                    // generate module-info.java
+                    System.out.format("writing to %s%n", file);
+                    writeModuleInfo(file,  explicitModule.descriptor());
                 } else {
-                    requires = new HashMap<>();
+                    // find missing dependences
+                    System.out.format("Missing dependence: %s not generated%n", file);
+                    missingDeps = true;
                 }
-                analyzer.requires(m)
-                    .map(Archive::getModule)
-                    .forEach(d -> requires.putIfAbsent(d.name(), Boolean.FALSE));
-
-                strictModules.put(m, m.toStrictModule(requires));
-            });
-
-            // generate module-info.java
-            descriptors().forEach(md -> writeModuleInfo(outputdir, md));
-
-            // done parsing
-            for (Module m : automaticModules()) {
-                m.close();
             }
 
-            // find any missing dependences
-            return automaticModules().stream()
-                        .flatMap(analyzer::requires)
-                        .allMatch(m -> !m.equals(NOT_FOUND));
+            return !missingDeps;
         } finally {
             dependencyFinder.shutdown();
         }
     }
 
+    boolean notFound(Archive m) {
+        return m == NOT_FOUND || m == REMOVED_JDK_INTERNALS;
+    }
+
+    private Module toExplicitModule(Module module, Set<Archive> requiresPublic)
+        throws IOException
+    {
+        // done analysis
+        module.close();
+
+        if (analyzer.requires(module).anyMatch(this::notFound)) {
+            // missing dependencies
+            return null;
+        }
+
+        Map<String, Boolean> requires = new HashMap<>();
+        requiresPublic.stream()
+            .map(Archive::getModule)
+            .forEach(m -> requires.put(m.name(), Boolean.TRUE));
+
+        analyzer.requires(module)
+            .map(Archive::getModule)
+            .forEach(d -> requires.putIfAbsent(d.name(), Boolean.FALSE));
+
+        return module.toStrictModule(requires);
+    }
+
     /**
      * Returns the stream of resulting modules
      */
     Stream<Module> modules() {
-        return strictModules.values().stream();
+        return automaticToExplicitModule.values().stream();
     }
 
     /**
      * Returns the stream of resulting ModuleDescriptors
      */
     public Stream<ModuleDescriptor> descriptors() {
-        return strictModules.values().stream().map(Module::descriptor);
+        return automaticToExplicitModule.entrySet().stream()
+                    .map(Map.Entry::getValue)
+                    .map(Module::descriptor);
     }
 
     void visitMissingDeps(Analyzer.Visitor visitor) {
         automaticModules().stream()
-            .filter(m -> analyzer.requires(m).anyMatch(d -> d.equals(NOT_FOUND)))
+            .filter(m -> analyzer.requires(m).anyMatch(this::notFound))
             .forEach(m -> {
                 analyzer.visitDependences(m, visitor, Analyzer.Type.VERBOSE);
             });
     }
-    void writeModuleInfo(Path dir, ModuleDescriptor descriptor) {
-        String mn = descriptor.name();
-        Path srcFile = dir.resolve(mn).resolve("module-info.java");
+
+    void writeModuleInfo(Path file, ModuleDescriptor descriptor) {
         try {
-            Files.createDirectories(srcFile.getParent());
-            System.out.println("writing to " + srcFile);
-            try (PrintWriter pw = new PrintWriter(Files.newOutputStream(srcFile))) {
+            Files.createDirectories(file.getParent());
+            try (PrintWriter pw = new PrintWriter(Files.newOutputStream(file))) {
                 printModuleInfo(pw, descriptor);
             }
         } catch (IOException e) {
@@ -197,7 +223,7 @@
 
 
     private Set<Module> automaticModules() {
-        return strictModules.keySet();
+        return automaticToExplicitModule.keySet();
     }
 
     /**
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties	Fri May 20 11:55:46 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties	Fri May 20 12:24:02 2016 -0700
@@ -11,10 +11,9 @@
 java.awt.peer=Should not use. See https://bugs.openjdk.java.net/browse/JDK-8037739
 java.awt.dnd.peer=Should not use. See https://bugs.openjdk.java.net/browse/JDK-8037739
 jdk.internal.ref.Cleaner=Use java.lang.ref.PhantomReference @since 1.2 or java.lang.ref.Cleaner @since 9
-sun.awt.image.codec=Use javax.imageio @since 1.4
 sun.awt.CausedFocusEvent=Use java.awt.event.FocusEvent::getCause @since 9
 sun.font.FontUtilities=See java.awt.Font.textRequiresLayout	@since 9
-sun.reflect.Reflection=See StackWalker API @since 9
+sun.reflect.Reflection=Use java.lang.StackWalker @since 9
 sun.reflect.ReflectionFactory=See http://openjdk.java.net/jeps/260
 sun.misc.Unsafe=See http://openjdk.java.net/jeps/260
 sun.misc.Signal=See http://openjdk.java.net/jeps/260
@@ -25,11 +24,12 @@
 sun.security.provider.Sun=Use java.security.Security.getProvider(provider-name) @since 1.3
 sun.security.util.SecurityConstants=Use appropriate java.security.Permission subclass @since 1.1
 sun.security.x509.X500Name=Use javax.security.auth.x500.X500Principal @since 1.4
-sun.tools.jar=Use java.util.jar or jar tool @since 1.2\
+sun.tools.jar=Use java.util.jar or jar tool @since 1.2
 # Internal APIs removed in JDK 9
 com.apple.eawt=Use java.awt.desktop and JEP 272 @since 9
 com.apple.concurrent=Removed. See https://bugs.openjdk.java.net/browse/JDK-8148187
-com.sun.image.codec=Use javax.imageio @since 1.4
+com.sun.image.codec.jpeg=Use javax.imageio @since 1.4
+sun.awt.image.codec=Use javax.imageio @since 1.4
 sun.misc.BASE64Encoder=Use java.util.Base64 @since 1.8
 sun.misc.BASE64Decoder=Use java.util.Base64 @since 1.8
 sun.misc.Cleaner=Use java.lang.ref.PhantomReference @since 1.2 or java.lang.ref.Cleaner @since 9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/jdkinternals/RemovedJDKInternals.java	Fri May 20 12:24:02 2016 -0700
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8153042
+ * @summary Tests JDK internal APIs that have been removed.
+ * @library ../lib
+ * @build CompilerUtils JdepsUtil ModuleMetaData
+ * @modules jdk.jdeps/com.sun.tools.jdeps
+ * @run testng RemovedJDKInternals
+ */
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+
+import com.sun.tools.jdeps.DepsAnalyzer;
+import com.sun.tools.jdeps.Graph;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+public class RemovedJDKInternals {
+    private static final String TEST_SRC = System.getProperty("test.src");
+
+    private static final Path CLASSES_DIR = Paths.get("classes");
+    private static final Path PATCHES_DIR = Paths.get("patches");
+
+    private static final String JDK_UNSUPPORTED = "jdk.unsupported";
+    /**
+     * Compiles classes used by the test
+     */
+    @BeforeTest
+    public void compileAll() throws Exception {
+        CompilerUtils.cleanDir(PATCHES_DIR);
+        CompilerUtils.cleanDir(CLASSES_DIR);
+
+        // compile sun.misc types
+        Path sunMiscSrc = Paths.get(TEST_SRC, "patches", JDK_UNSUPPORTED);
+        Path patchDir = PATCHES_DIR.resolve(JDK_UNSUPPORTED);
+        assertTrue(CompilerUtils.compile(sunMiscSrc, patchDir,
+                                         "-Xmodule:" + JDK_UNSUPPORTED));
+
+        // compile com.sun.image.codec.jpeg types
+        Path codecSrc = Paths.get(TEST_SRC, "patches", "java.desktop");
+        Path codecDest = PATCHES_DIR;
+        assertTrue(CompilerUtils.compile(codecSrc, codecDest));
+
+        // patch jdk.unsupported and set -cp to codec types
+        assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "src"),
+                                         CLASSES_DIR,
+                                         "-Xpatch:jdk.unsupported=" + patchDir,
+                                         "-cp", codecDest.toString()));
+    }
+
+    @DataProvider(name = "deps")
+    public Object[][] deps() {
+        return new Object[][] {
+            { "classes", new ModuleMetaData("classes", false)
+                .reference("p.Main", "java.lang.Class", "java.base")
+                .reference("p.Main", "java.lang.Object", "java.base")
+                .reference("p.Main", "java.util.Iterator", "java.base")
+                .reference("p.S", "java.lang.Object", "java.base")
+                .jdkInternal("p.Main", "sun.reflect.Reflection", "jdk.unsupported")
+                .removedJdkInternal("p.Main", "com.sun.image.codec.jpeg.JPEGCodec")
+                .removedJdkInternal("p.Main", "sun.misc.Service")
+                .removedJdkInternal("p.Main", "sun.misc.SoftCache")
+            },
+        };
+    }
+
+    @Test(dataProvider = "deps")
+    public void runTest(String name, ModuleMetaData data) throws Exception {
+        String cmd = String.format("jdeps -verbose:class %s%n", CLASSES_DIR);
+        try (JdepsUtil.Command jdeps = JdepsUtil.newCommand(cmd)) {
+            jdeps.verbose("-verbose:class")
+                .addRoot(CLASSES_DIR);
+
+            DepsAnalyzer analyzer = jdeps.getDepsAnalyzer();
+            assertTrue(analyzer.run());
+            jdeps.dumpOutput(System.err);
+
+            Graph<DepsAnalyzer.Node> g = analyzer.dependenceGraph();
+            // there are two node with p.Main as origin
+            // one for exported API and one for removed JDK internal
+            g.nodes().stream()
+                .filter(u -> u.source.equals(data.moduleName))
+                .forEach(u -> g.adjacentNodes(u).stream()
+                    .forEach(v -> data.checkDependence(u.name, v.name, v.source, v.info)));
+        }
+    }
+
+    private static final Map<String, String> REPLACEMENTS = Map.of(
+        "com.sun.image.codec.jpeg.JPEGCodec", "Use javax.imageio @since 1.4",
+        "sun.misc.Service", "Use java.util.ServiceLoader @since 1.6",
+        "sun.misc.SoftCache", "Removed. See http://openjdk.java.net/jeps/260",
+        "sun.reflect.Reflection", "Use java.lang.StackWalker @since 9"
+    );
+
+    @Test
+    public void checkReplacement() {
+        String[] output = JdepsUtil.jdeps("-jdkinternals", CLASSES_DIR.toString());
+        int i = 0;
+        while (!output[i].contains("Suggested Replacement")) {
+            i++;
+        }
+
+        // must match the number of JDK internal APIs
+        int count = output.length-i-2;
+        assertEquals(count, REPLACEMENTS.size());
+
+        for (int j=i+2; j < output.length; j++) {
+            String line = output[j];
+            int pos = line.indexOf("Use ");
+            if (pos < 0)
+                pos = line.indexOf("Removed. ");
+
+            assertTrue(pos > 0);
+            String name = line.substring(0, pos).trim();
+            String repl = line.substring(pos, line.length()).trim();
+            assertEquals(REPLACEMENTS.get(name), repl);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/jdkinternals/patches/java.desktop/com/sun/image/codec/jpeg/JPEGCodec.java	Fri May 20 12:24:02 2016 -0700
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.image.codec.jpeg;
+
+/*
+ * JDK removed internal API
+ */
+public class JPEGCodec {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/jdkinternals/patches/jdk.unsupported/sun/misc/Service.java	Fri May 20 12:24:02 2016 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.misc;
+
+import java.util.Iterator;
+
+/*
+ * JDK removed internal API
+ */
+public final class Service<S> {
+    public static <S> Iterator<S> providers(Class<S> service) {
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/jdkinternals/patches/jdk.unsupported/sun/misc/SoftCache.java	Fri May 20 12:24:02 2016 -0700
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.misc;
+
+/*
+ * JDK removed internal API
+ */
+public class SoftCache {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/jdkinternals/src/p/Main.java	Fri May 20 12:24:02 2016 -0700
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package p;
+
+import com.sun.image.codec.jpeg.JPEGCodec;
+import sun.misc.Service;
+import sun.misc.SoftCache;
+import sun.reflect.Reflection;
+
+public class Main {
+    public static void main() {
+        // in jdk.unsupported
+        Class<?> caller = Reflection.getCallerClass(2);
+
+        // removed
+        JPEGCodec r = new JPEGCodec();
+
+        // removed
+        SoftCache s = new SoftCache();
+
+        // removed
+        Service.providers(S.class);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/jdkinternals/src/p/S.java	Fri May 20 12:24:02 2016 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package p;
+
+public interface S {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/jdeps/lib/ModuleMetaData.java	Fri May 20 12:24:02 2016 -0700
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+import com.sun.tools.jdeps.DepsAnalyzer;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import static com.sun.tools.jdeps.DepsAnalyzer.Info.*;
+import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
+import static java.lang.module.ModuleDescriptor.*;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+
+public class ModuleMetaData {
+    public static final String JAVA_BASE = "java.base";
+
+    static final String INTERNAL = "(internal)";
+    static final String QUALIFIED = "(qualified)";
+    static final String JDK_INTERNAL = "JDK internal API";
+    static final String REMOVED_JDK_INTERNAL = "JDK removed internal API";
+
+    final String moduleName;
+    final boolean isNamed;
+    final Map<String, ModuleRequires> requires = new LinkedHashMap<>();
+    final Map<String, Dependence> references = new LinkedHashMap<>();
+    final Map<String, Set<String>> exports = new LinkedHashMap<>();
+
+    ModuleMetaData(String name) {
+        this(name, true);
+    }
+
+    ModuleMetaData(String name, boolean isNamed) {
+        this.moduleName = name;
+        this.isNamed = isNamed;
+        requires(JAVA_BASE);  // implicit requires
+    }
+
+    String name() {
+        return moduleName;
+    }
+
+    ModuleMetaData requires(String name) {
+        requires.put(name, new ModuleRequires(name));
+        return this;
+    }
+
+    ModuleMetaData requiresPublic(String name) {
+        requires.put(name, new ModuleRequires(name, PUBLIC));
+        return this;
+    }
+
+    // for unnamed module
+    ModuleMetaData depends(String name) {
+        requires.put(name, new ModuleRequires(name));
+        return this;
+    }
+
+    ModuleMetaData reference(String origin, String target, String module) {
+        return dependence(origin, target, module, "");
+    }
+
+    ModuleMetaData internal(String origin, String target, String module) {
+        return dependence(origin, target, module, INTERNAL);
+    }
+
+    ModuleMetaData qualified(String origin, String target, String module) {
+        return dependence(origin, target, module, QUALIFIED);
+    }
+
+    ModuleMetaData jdkInternal(String origin, String target, String module) {
+        return dependence(origin, target, module, JDK_INTERNAL);
+    }
+    ModuleMetaData removedJdkInternal(String origin, String target) {
+        return dependence(origin, target, REMOVED_JDK_INTERNAL, REMOVED_JDK_INTERNAL);
+    }
+
+    ModuleMetaData exports(String pn, Set<String> targets) {
+        exports.put(pn, targets);
+        return this;
+    }
+
+    private ModuleMetaData dependence(String origin, String target, String module, String access) {
+        references.put(key(origin, target), new Dependence(origin, target, module, access));
+        return this;
+    }
+
+    String key(String origin, String target) {
+        return origin + ":" + target;
+    }
+
+    void checkRequires(String name, Set<DepsAnalyzer.Node> adjacentNodes) {
+        // System.err.format("%s: Expected %s Found %s %n", name, requires, adjacentNodes);
+        adjacentNodes.stream()
+            .forEach(v -> checkRequires(v.name));
+        assertEquals(adjacentNodes.size(), requires.size());
+    }
+
+    void checkRequires(String name) {
+        ModuleRequires req = requires.get(name);
+        if (req == null)
+            System.err.println(moduleName + ": unexpected requires " + name);
+        assertTrue(requires.containsKey(name));
+    }
+
+    void checkRequires(Requires require) {
+        String name = require.name();
+        if (name.equals(JAVA_BASE))
+            return;
+
+        ModuleRequires req = requires.get(name);
+        if (req == null)
+            System.err.format("%s: unexpected dependence %s%n", moduleName, name);
+
+        assertTrue(requires.containsKey(name));
+
+        assertEquals(require.modifiers(), req.modifiers());
+    }
+
+    void checkDependences(String name, Set<DepsAnalyzer.Node> adjacentNodes) {
+        // System.err.format("%s: Expected %s Found %s %n", name, references, adjacentNodes);
+
+        adjacentNodes.stream()
+            .forEach(v -> checkDependence(name, v.name, v.source, v.info));
+        assertEquals(adjacentNodes.size(), references.size());
+    }
+
+    void checkDependence(String origin, String target, String module, DepsAnalyzer.Info info) {
+        String key = key(origin, target);
+        Dependence dep = references.get(key);
+        String access = "";
+        if (info == QUALIFIED_EXPORTED_API)
+            access = QUALIFIED;
+        else if (info == JDK_INTERNAL_API)
+            access = JDK_INTERNAL;
+        else if (info == JDK_REMOVED_INTERNAL_API)
+            access = REMOVED_JDK_INTERNAL;
+        else if (info == INTERNAL_API)
+            access = INTERNAL;
+
+        assertTrue(references.containsKey(key));
+
+        assertEquals(dep.access, access);
+        assertEquals(dep.module, module);
+    }
+
+
+    public static class ModuleRequires {
+        final String name;
+        final Requires.Modifier mod;
+
+        ModuleRequires(String name) {
+            this.name = name;
+            this.mod = null;
+        }
+
+        ModuleRequires(String name, Requires.Modifier mod) {
+            this.name = name;
+            this.mod = mod;
+        }
+
+        Set<Requires.Modifier> modifiers() {
+            return mod != null ? Set.of(mod) : Collections.emptySet();
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+    }
+
+    public static class Dependence {
+        final String origin;
+        final String target;
+        final String module;
+        final String access;
+
+        Dependence(String origin, String target, String module, String access) {
+            this.origin = origin;
+            this.target = target;
+            this.module = module;
+            this.access = access;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s -> %s (%s) %s", origin, target, module, access);
+        }
+    }
+}
--- a/langtools/test/tools/jdeps/modules/ModuleMetaData.java	Fri May 20 11:55:46 2016 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,209 +0,0 @@
-/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-
-import com.sun.tools.jdeps.DepsAnalyzer;
-
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Set;
-
-import static com.sun.tools.jdeps.DepsAnalyzer.Info.*;
-import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
-import static java.lang.module.ModuleDescriptor.*;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
-
-
-public class ModuleMetaData {
-    public static final String JAVA_BASE = "java.base";
-
-    static final String INTERNAL = "(internal)";
-    static final String QUALIFIED = "(qualified)";
-    static final String JDK_INTERNAL = "JDK internal API";
-
-    final String moduleName;
-    final boolean isNamed;
-    final Map<String, ModuleRequires> requires = new LinkedHashMap<>();
-    final Map<String, Dependence> references = new LinkedHashMap<>();
-    final Map<String, Set<String>> exports = new LinkedHashMap<>();
-
-    ModuleMetaData(String name) {
-        this(name, true);
-    }
-
-    ModuleMetaData(String name, boolean isNamed) {
-        this.moduleName = name;
-        this.isNamed = isNamed;
-        requires(JAVA_BASE);  // implicit requires
-    }
-
-    String name() {
-        return moduleName;
-    }
-
-    ModuleMetaData requires(String name) {
-        requires.put(name, new ModuleRequires(name));
-        return this;
-    }
-
-    ModuleMetaData requiresPublic(String name) {
-        requires.put(name, new ModuleRequires(name, PUBLIC));
-        return this;
-    }
-
-    // for unnamed module
-    ModuleMetaData depends(String name) {
-        requires.put(name, new ModuleRequires(name));
-        return this;
-    }
-
-    ModuleMetaData reference(String origin, String target, String module) {
-        return dependence(origin, target, module, "");
-    }
-
-    ModuleMetaData internal(String origin, String target, String module) {
-        return dependence(origin, target, module, INTERNAL);
-    }
-
-    ModuleMetaData qualified(String origin, String target, String module) {
-        return dependence(origin, target, module, QUALIFIED);
-    }
-
-    ModuleMetaData jdkInternal(String origin, String target, String module) {
-        return dependence(origin, target, module, JDK_INTERNAL);
-    }
-
-    ModuleMetaData exports(String pn, Set<String> targets) {
-        exports.put(pn, targets);
-        return this;
-    }
-
-    private ModuleMetaData dependence(String origin, String target, String module, String access) {
-        references.put(key(origin, target), new Dependence(origin, target, module, access));
-        return this;
-    }
-
-    String key(String origin, String target) {
-        return origin + ":" + target;
-    }
-
-    void checkRequires(String name, Set<DepsAnalyzer.Node> adjacentNodes) {
-        // System.err.format("%s: Expected %s Found %s %n", name, requires, adjacentNodes);
-        adjacentNodes.stream()
-            .forEach(v -> checkRequires(v.name));
-        assertEquals(adjacentNodes.size(), requires.size());
-    }
-
-    void checkRequires(String name) {
-        ModuleRequires req = requires.get(name);
-        if (req == null)
-            System.err.println(moduleName + ": unexpected requires " + name);
-        assertTrue(requires.containsKey(name));
-    }
-
-    void checkRequires(Requires require) {
-        String name = require.name();
-        if (name.equals(JAVA_BASE))
-            return;
-
-        ModuleRequires req = requires.get(name);
-        if (req == null)
-            System.err.format("%s: unexpected dependence %s%n", moduleName, name);
-
-        assertTrue(requires.containsKey(name));
-
-        assertEquals(require.modifiers(), req.modifiers());
-    }
-
-    void checkDependences(String name, Set<DepsAnalyzer.Node> adjacentNodes) {
-        // System.err.format("%s: Expected %s Found %s %n", name, references, adjacentNodes);
-
-        adjacentNodes.stream()
-            .forEach(v -> checkDependence(name, v.name, v.source, v.info));
-        assertEquals(adjacentNodes.size(), references.size());
-    }
-
-    void checkDependence(String origin, String target, String module, DepsAnalyzer.Info info) {
-        String key = key(origin, target);
-        Dependence dep = references.get(key);
-        String access = "";
-        if (info == QUALIFIED_EXPORTED_API)
-            access = QUALIFIED;
-        else if (info == JDK_INTERNAL_API)
-            access = JDK_INTERNAL;
-        else if (info == INTERNAL_API)
-            access = INTERNAL;
-
-        assertTrue(references.containsKey(key));
-
-        assertEquals(dep.access, access);
-        assertEquals(dep.module, module);
-    }
-
-
-    public static class ModuleRequires {
-        final String name;
-        final Requires.Modifier mod;
-
-        ModuleRequires(String name) {
-            this.name = name;
-            this.mod = null;
-        }
-
-        ModuleRequires(String name, Requires.Modifier mod) {
-            this.name = name;
-            this.mod = mod;
-        }
-
-        Set<Requires.Modifier> modifiers() {
-            return mod != null ? Set.of(mod) : Collections.emptySet();
-        }
-
-        @Override
-        public String toString() {
-            return name;
-        }
-    }
-
-    public static class Dependence {
-        final String origin;
-        final String target;
-        final String module;
-        final String access;
-
-        Dependence(String origin, String target, String module, String access) {
-            this.origin = origin;
-            this.target = target;
-            this.module = module;
-            this.access = access;
-        }
-
-        @Override
-        public String toString() {
-            return String.format("%s -> %s (%s) %s", origin, target, module, access);
-        }
-    }
-}