1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 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 */ 123 List<String> getPackageProperties(final String pkg) { 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. 199 // Dynalink supported public nested classes as properties of 200 // StaticClass object anyway. We don't want to expose those 201 // "$" internal names as properties of package object. 202 203 final String clsName = binaryName.substring(start); 204 if (clsName.indexOf('$') == -1) { 205 props.add(clsName); 206 } 207 } 208 } 209 } 210 211 // return list of File objects for the given class path 212 private static List<File> getFiles(final String classPath) { 213 return Stream.of(classPath.split(File.pathSeparator)) 214 .map(File::new) 215 .collect(Collectors.toList()); 216 } 217 } --- EOF ---