10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.tools.jjs; 27 28 import java.io.IOException; 29 import java.io.File; 30 import java.util.ArrayList; 31 import java.util.Collections; 32 import java.util.EnumSet; 33 import java.util.HashSet; 34 import java.util.LinkedHashMap; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.Set; 38 import java.util.stream.Collectors; 39 import java.util.stream.Stream; 40 import javax.tools.JavaCompiler; 41 import javax.tools.JavaFileManager.Location; 42 import javax.tools.JavaFileObject; 43 import javax.tools.StandardJavaFileManager; 44 import javax.tools.StandardLocation; 45 import javax.tools.ToolProvider; 46 47 /** 48 * A helper class to compute properties of a Java package object. Properties of 49 * package object are (simple) top level class names in that java package and 50 * immediate subpackages of that package. 51 */ 52 final class PackagesHelper { 53 // JavaCompiler may be null on certain platforms (eg. JRE) 54 private static final JavaCompiler compiler; 55 static { 56 // Use javac only if security manager is not around! 57 compiler = System.getSecurityManager() == null? ToolProvider.getSystemJavaCompiler() : null; 58 } 59 60 /** 61 * Is Java package properties helper available? 62 * 63 * @return true if package properties support is available 64 */ 65 static boolean isAvailable() { 66 return compiler != null; 67 } 68 69 private final StandardJavaFileManager fm; 70 private final Set<JavaFileObject.Kind> fileKinds; 71 72 /** 73 * Construct a new PackagesHelper. 74 * 75 * @param classPath Class path to compute properties of java package objects 76 */ 77 PackagesHelper(final String classPath) throws IOException { 78 assert isAvailable() : "no java compiler found!"; 79 80 fm = compiler.getStandardFileManager(null, null, null); 81 fileKinds = EnumSet.of(JavaFileObject.Kind.CLASS); 82 83 if (classPath != null && !classPath.isEmpty()) { 84 fm.setLocation(StandardLocation.CLASS_PATH, getFiles(classPath)); 85 } else { 86 // no classpath set. Make sure that it is empty and not any default like "." 87 fm.setLocation(StandardLocation.CLASS_PATH, Collections.<File>emptyList()); 88 } 89 } 90 91 // LRU cache for java package properties lists 92 private final LinkedHashMap<String, List<String>> propsCache = 93 new LinkedHashMap<String, List<String>>(32, 0.75f, true) { 94 private static final int CACHE_SIZE = 100; 95 private static final long serialVersionUID = 1; 96 97 @Override 98 protected boolean removeEldestEntry(final Map.Entry<String, List<String>> eldest) { 99 return size() > CACHE_SIZE; 100 } 101 }; 102 103 /** 104 * Return the list of properties of the given Java package or package prefix 105 * 106 * @param pkg Java package name or package prefix name 107 * @return the list of properties of the given Java package or package prefix 108 */ 110 // check the cache first 111 if (propsCache.containsKey(pkg)) { 112 return propsCache.get(pkg); 113 } 114 115 try { 116 // make sorted list of properties 117 final List<String> props = new ArrayList<>(listPackage(pkg)); 118 Collections.sort(props); 119 propsCache.put(pkg, props); 120 return props; 121 } catch (final IOException exp) { 122 if (Main.DEBUG) { 123 exp.printStackTrace(); 124 } 125 return Collections.<String>emptyList(); 126 } 127 } 128 129 public void close() throws IOException { 130 fm.close(); 131 } 132 133 private Set<String> listPackage(final String pkg) throws IOException { 134 final Set<String> props = new HashSet<>(); 135 listPackage(StandardLocation.PLATFORM_CLASS_PATH, pkg, props); 136 listPackage(StandardLocation.CLASS_PATH, pkg, props); 137 return props; 138 } 139 140 private void listPackage(final Location loc, final String pkg, final Set<String> props) 141 throws IOException { 142 for (JavaFileObject file : fm.list(loc, pkg, fileKinds, true)) { 143 final String binaryName = fm.inferBinaryName(loc, file); 144 // does not start with the given package prefix 145 if (!binaryName.startsWith(pkg + ".")) { 146 continue; 147 } 148 149 final int nextDot = binaryName.indexOf('.', pkg.length() + 1); 150 final int start = pkg.length() + 1; 151 152 if (nextDot != -1) { 153 // subpackage - eg. "regex" for "java.util" 154 props.add(binaryName.substring(start, nextDot)); 155 } else { 156 // class - filter out nested, inner, anonymous, local classes. | 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.tools.jjs; 27 28 import java.io.IOException; 29 import java.io.File; 30 import java.net.URI; 31 import java.nio.file.DirectoryStream; 32 import java.nio.file.Files; 33 import java.nio.file.FileSystem; 34 import java.nio.file.FileSystems; 35 import java.nio.file.Path; 36 import java.util.ArrayList; 37 import java.util.Collections; 38 import java.util.EnumSet; 39 import java.util.HashSet; 40 import java.util.LinkedHashMap; 41 import java.util.List; 42 import java.util.Map; 43 import java.util.Set; 44 import java.util.stream.Collectors; 45 import java.util.stream.Stream; 46 import javax.tools.JavaCompiler; 47 import javax.tools.JavaFileManager.Location; 48 import javax.tools.JavaFileObject; 49 import javax.tools.StandardJavaFileManager; 50 import javax.tools.StandardLocation; 51 import javax.tools.ToolProvider; 52 53 /** 54 * A helper class to compute properties of a Java package object. Properties of 55 * package object are (simple) top level class names in that java package and 56 * immediate subpackages of that package. 57 */ 58 final class PackagesHelper { 59 // JavaCompiler may be null on certain platforms (eg. JRE) 60 private static final JavaCompiler compiler; 61 static { 62 // Use javac only if security manager is not around! 63 compiler = System.getSecurityManager() == null? ToolProvider.getSystemJavaCompiler() : null; 64 } 65 66 /** 67 * Is javac available? 68 * 69 * @return true if javac is available 70 */ 71 private static boolean isJavacAvailable() { 72 return compiler != null; 73 } 74 75 private final StandardJavaFileManager fm; 76 private final Set<JavaFileObject.Kind> fileKinds; 77 private final FileSystem jrtfs; 78 79 /** 80 * Construct a new PackagesHelper. 81 * 82 * @param classPath Class path to compute properties of java package objects 83 */ 84 PackagesHelper(final String classPath) throws IOException { 85 if (isJavacAvailable()) { 86 fm = compiler.getStandardFileManager(null, null, null); 87 fileKinds = EnumSet.of(JavaFileObject.Kind.CLASS); 88 89 if (classPath != null && !classPath.isEmpty()) { 90 fm.setLocation(StandardLocation.CLASS_PATH, getFiles(classPath)); 91 } else { 92 // no classpath set. Make sure that it is empty and not any default like "." 93 fm.setLocation(StandardLocation.CLASS_PATH, Collections.<File>emptyList()); 94 } 95 jrtfs = null; 96 } else { 97 // javac is not available - directly use jrt fs 98 // to support atleast platform classes. 99 fm = null; 100 fileKinds = null; 101 jrtfs = FileSystems.getFileSystem(URI.create("jrt:/")); 102 } 103 } 104 105 // LRU cache for java package properties lists 106 private final LinkedHashMap<String, List<String>> propsCache = 107 new LinkedHashMap<String, List<String>>(32, 0.75f, true) { 108 private static final int CACHE_SIZE = 100; 109 private static final long serialVersionUID = 1; 110 111 @Override 112 protected boolean removeEldestEntry(final Map.Entry<String, List<String>> eldest) { 113 return size() > CACHE_SIZE; 114 } 115 }; 116 117 /** 118 * Return the list of properties of the given Java package or package prefix 119 * 120 * @param pkg Java package name or package prefix name 121 * @return the list of properties of the given Java package or package prefix 122 */ 124 // check the cache first 125 if (propsCache.containsKey(pkg)) { 126 return propsCache.get(pkg); 127 } 128 129 try { 130 // make sorted list of properties 131 final List<String> props = new ArrayList<>(listPackage(pkg)); 132 Collections.sort(props); 133 propsCache.put(pkg, props); 134 return props; 135 } catch (final IOException exp) { 136 if (Main.DEBUG) { 137 exp.printStackTrace(); 138 } 139 return Collections.<String>emptyList(); 140 } 141 } 142 143 public void close() throws IOException { 144 if (fm != null) { 145 fm.close(); 146 } 147 } 148 149 private Set<String> listPackage(final String pkg) throws IOException { 150 final Set<String> props = new HashSet<>(); 151 if (fm != null) { 152 listPackage(StandardLocation.PLATFORM_CLASS_PATH, pkg, props); 153 listPackage(StandardLocation.CLASS_PATH, pkg, props); 154 } else if (jrtfs != null) { 155 // look for the /packages/<package_name> directory 156 Path pkgDir = jrtfs.getPath("/packages/" + pkg); 157 if (Files.exists(pkgDir)) { 158 String pkgSlashName = pkg.replace('.', '/'); 159 try (DirectoryStream<Path> ds = Files.newDirectoryStream(pkgDir)) { 160 // it has module links under which this package occurs 161 for (Path mod : ds) { 162 // get the package directory under /modules 163 Path pkgUnderMod = jrtfs.getPath(mod.toString() + "/" + pkgSlashName); 164 try (DirectoryStream<Path> ds2 = Files.newDirectoryStream(pkgUnderMod)) { 165 for (Path p : ds2) { 166 String str = p.getFileName().toString(); 167 // get rid of ".class", if any 168 if (str.endsWith(".class")) { 169 props.add(str.substring(0, str.length() - ".class".length())); 170 } else { 171 props.add(str); 172 } 173 } 174 } 175 } 176 } 177 } 178 } 179 return props; 180 } 181 182 private void listPackage(final Location loc, final String pkg, final Set<String> props) 183 throws IOException { 184 for (JavaFileObject file : fm.list(loc, pkg, fileKinds, true)) { 185 final String binaryName = fm.inferBinaryName(loc, file); 186 // does not start with the given package prefix 187 if (!binaryName.startsWith(pkg + ".")) { 188 continue; 189 } 190 191 final int nextDot = binaryName.indexOf('.', pkg.length() + 1); 192 final int start = pkg.length() + 1; 193 194 if (nextDot != -1) { 195 // subpackage - eg. "regex" for "java.util" 196 props.add(binaryName.substring(start, nextDot)); 197 } else { 198 // class - filter out nested, inner, anonymous, local classes. |