hotspot/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/collect/ClassCollector.java
changeset 43680 dd3ea9044937
parent 43379 f4aff695ffe0
parent 43490 3c43be5db478
child 43681 b810b7f4fdeb
equal deleted inserted replaced
43379:f4aff695ffe0 43680:dd3ea9044937
     1 /*
       
     2  * Copyright (c) 2016, 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.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 package jdk.tools.jaotc.collect;
       
    25 
       
    26 import jdk.tools.jaotc.LogPrinter;
       
    27 import jdk.tools.jaotc.Main;
       
    28 
       
    29 import java.io.File;
       
    30 import java.io.IOException;
       
    31 import java.net.*;
       
    32 import java.nio.file.*;
       
    33 import java.nio.file.attribute.BasicFileAttributes;
       
    34 import java.util.*;
       
    35 
       
    36 import static java.nio.file.FileVisitResult.CONTINUE;
       
    37 
       
    38 public class ClassCollector {
       
    39     private final Main.Options options;
       
    40     private final LogPrinter log;
       
    41 
       
    42     public ClassCollector(Main.Options options, LogPrinter log) {
       
    43         this.options = options;
       
    44         this.log = log;
       
    45     }
       
    46 
       
    47     /**
       
    48      * Collect all class names passed by the user.
       
    49      *
       
    50      * @return array list of classes
       
    51      */
       
    52     public Set<Class<?>> collectClassesToCompile() {
       
    53         Set<Class<?>> classes = new HashSet<>();
       
    54         List<String> filesToScan = new LinkedList<>(options.files);
       
    55 
       
    56         if (options.module != null) {
       
    57             classes.addAll(scanModule(filesToScan));
       
    58         }
       
    59 
       
    60         classes.addAll(scanFiles(filesToScan));
       
    61         return classes;
       
    62     }
       
    63 
       
    64     private Set<Class<?>> scanModule(List<String> filesToScan) {
       
    65         String module = options.module;
       
    66         // Search module in standard JDK installation.
       
    67         Path dir = getModuleDirectory(options.modulepath, module);
       
    68 
       
    69         if (Files.isDirectory(dir)) {
       
    70             return loadFromModuleDirectory(dir);
       
    71         } else {
       
    72             findFilesToScan(filesToScan, module);
       
    73             return new HashSet<>();
       
    74         }
       
    75     }
       
    76 
       
    77     private Set<Class<?>> loadFromModuleDirectory(Path dir) {
       
    78         log.printInfo("Scanning module: " + dir + " ...");
       
    79         log.printlnVerbose(" "); // Break line
       
    80 
       
    81         FileSystemFinder finder = new FileSystemFinder(dir, pathname -> entryIsClassFile(pathname.toString()));
       
    82         Set<Class<?>> cls = loadWithClassLoader(() -> ClassLoader.getSystemClassLoader(), dir, finder);
       
    83         log.printlnInfo(" " + cls.size() + " classes loaded.");
       
    84         return cls;
       
    85     }
       
    86 
       
    87     private void findFilesToScan(List<String> filesToScan, String module) {
       
    88         // Try to search regular directory, .jar or .class files
       
    89         Path path = Paths.get(options.modulepath, module);
       
    90 
       
    91         if (Files.isDirectory(path)) {
       
    92             filesToScan.add(".");
       
    93             options.classpath = path.toString();
       
    94         } else if (path.endsWith(".jar") || path.endsWith(".class")) {
       
    95             filesToScan.add(path.toString());
       
    96         } else {
       
    97             path = Paths.get(options.modulepath, module + ".jar");
       
    98             if (Files.exists(path)) {
       
    99                 filesToScan.add(path.toString());
       
   100             } else {
       
   101                 path = Paths.get(options.modulepath, module + ".class");
       
   102                 if (Files.exists(path)) {
       
   103                     filesToScan.add(path.toString());
       
   104                 } else {
       
   105                     throw new InternalError("Expecting a .class, .jar or directory: " + path);
       
   106                 }
       
   107             }
       
   108         }
       
   109     }
       
   110 
       
   111     private boolean entryIsClassFile(String entry) {
       
   112         return entry.endsWith(".class") && !entry.endsWith("module-info.class");
       
   113     }
       
   114 
       
   115     private Set<Class<?>> scanFiles(List<String> filesToScan) {
       
   116         Set<Class<?>> classes = new HashSet<>();
       
   117         for (String fileName : filesToScan) {
       
   118             Set<Class<?>> loaded = scanFile(fileName);
       
   119             log.printlnInfo(" " + loaded.size() + " classes loaded.");
       
   120             classes.addAll(loaded);
       
   121         }
       
   122         return classes;
       
   123     }
       
   124 
       
   125     interface ClassLoaderFactory {
       
   126         ClassLoader create() throws IOException;
       
   127     }
       
   128 
       
   129     private Set<Class<?>> loadWithClassLoader(ClassLoaderFactory factory, Path root, FileSystemFinder finder) {
       
   130         ClassLoader loader = null;
       
   131         try {
       
   132             loader = factory.create();
       
   133             return loadClassFiles(root, finder, loader);
       
   134         } catch (IOException e) {
       
   135             throw new InternalError(e);
       
   136         } finally {
       
   137             if (loader instanceof AutoCloseable) {
       
   138                 try {
       
   139                     ((AutoCloseable) loader).close();
       
   140                 } catch (Exception e) {
       
   141                     throw new InternalError(e);
       
   142                 }
       
   143             }
       
   144         }
       
   145     }
       
   146 
       
   147     private Set<Class<?>> scanFile(String fileName) {
       
   148         log.printInfo("Scanning: " + fileName + " ...");
       
   149         log.printlnVerbose(" "); // Break line
       
   150 
       
   151         if (fileName.endsWith(".jar")) {
       
   152             return loadFromJarFile(fileName);
       
   153         } else if (fileName.endsWith(".class")) {
       
   154             Set<Class<?>> classes = new HashSet<>();
       
   155             loadFromClassFile(fileName, classes);
       
   156             return classes;
       
   157         } else {
       
   158             return scanClassPath(fileName);
       
   159         }
       
   160     }
       
   161 
       
   162     private Set<Class<?>> loadFromJarFile(String fileName) {
       
   163         FileSystem fs = makeFileSystem(fileName);
       
   164         FileSystemFinder finder = new FileSystemFinder(fs.getPath("/"), pathname -> entryIsClassFile(pathname.toString()));
       
   165         return loadWithClassLoader(() -> URLClassLoader.newInstance(buildUrls(fileName)), fs.getPath("/"), finder);
       
   166     }
       
   167 
       
   168     private void loadFromClassFile(String fileName, Set<Class<?>> classes) {
       
   169         Class<?> result;
       
   170         File file = new File(options.classpath);
       
   171         try (URLClassLoader loader = URLClassLoader.newInstance(buildUrls(file))) {
       
   172             result = loadClassFile(loader, fileName);
       
   173         } catch (IOException e) {
       
   174             throw new InternalError(e);
       
   175         }
       
   176         Class<?> c = result;
       
   177         addClass(classes, fileName, c);
       
   178     }
       
   179 
       
   180     private Set<Class<?>> scanClassPath(String fileName) {
       
   181         Path classPath = Paths.get(options.classpath);
       
   182         if (!Files.exists(classPath)) {
       
   183             throw new InternalError("Path does not exist: " + classPath);
       
   184         }
       
   185         if (!Files.isDirectory(classPath)) {
       
   186             throw new InternalError("Path must be a directory: " + classPath);
       
   187         }
       
   188 
       
   189         // Combine class path and file name and see what it is.
       
   190         Path combinedPath = Paths.get(options.classpath + File.separator + fileName);
       
   191         if (combinedPath.endsWith(".class")) {
       
   192             throw new InternalError("unimplemented");
       
   193         } else if (Files.isDirectory(combinedPath)) {
       
   194             return scanDirectory(classPath, combinedPath);
       
   195         } else {
       
   196             throw new InternalError("Expecting a .class, .jar or directory: " + fileName);
       
   197         }
       
   198     }
       
   199 
       
   200     private FileSystem makeFileSystem(String fileName) {
       
   201         try {
       
   202             return FileSystems.newFileSystem(makeJarFileURI(fileName), new HashMap<>());
       
   203         } catch (IOException e) {
       
   204             throw new InternalError(e);
       
   205         }
       
   206     }
       
   207 
       
   208     private URI makeJarFileURI(String fileName) {
       
   209         try {
       
   210             return new URI("jar:file:" + Paths.get(fileName).toAbsolutePath() + "!/");
       
   211         } catch (URISyntaxException e) {
       
   212             throw new InternalError(e);
       
   213         }
       
   214     }
       
   215 
       
   216     private PathMatcher combine(PathMatcher m1, PathMatcher m2) {
       
   217         return path -> m1.matches(path) && m2.matches(path);
       
   218     }
       
   219 
       
   220     private Set<Class<?>> scanDirectory(Path classPath, Path combinedPath) {
       
   221         String dir = options.classpath;
       
   222 
       
   223         FileSystem fileSystem = FileSystems.getDefault();
       
   224         PathMatcher matcher = fileSystem.getPathMatcher("glob:" + "*.class");
       
   225         FileSystemFinder finder = new FileSystemFinder(combinedPath,
       
   226             combine(matcher, pathname -> entryIsClassFile(pathname.toString())));
       
   227 
       
   228         File file = new File(dir);
       
   229         try (URLClassLoader loader = URLClassLoader.newInstance(buildUrls(file))) {
       
   230             return loadClassFiles(classPath, finder, loader);
       
   231         } catch (IOException e) {
       
   232             throw new InternalError(e);
       
   233         }
       
   234     }
       
   235 
       
   236     private Set<Class<?>> loadClassFiles(Path root, FileSystemFinder finder, ClassLoader loader) {
       
   237         Set<Class<?>> classes = new HashSet<>();
       
   238         for (Path name : finder.done()) {
       
   239             // Now relativize to the class path so we get the actual class names.
       
   240             String entry = root.relativize(name).normalize().toString();
       
   241             Class<?> c = loadClassFile(loader, entry);
       
   242             addClass(classes, entry, c);
       
   243         }
       
   244         return classes;
       
   245     }
       
   246 
       
   247     private void addClass(Set<Class<?>> classes, String name, Class<?> c) {
       
   248         if (c != null) {
       
   249             classes.add(c);
       
   250             log.printlnVerbose(" loaded " + name);
       
   251         }
       
   252     }
       
   253 
       
   254     private URL[] buildUrls(String fileName) throws MalformedURLException {
       
   255         return new URL[]{ new URL("jar:file:" + Paths.get(fileName).toAbsolutePath() + "!/") };
       
   256     }
       
   257 
       
   258     private URL[] buildUrls(File file) throws MalformedURLException {
       
   259         return new URL[] {file.toURI().toURL() };
       
   260     }
       
   261 
       
   262     private Path getModuleDirectory(String modulepath, String module) {
       
   263         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
       
   264         return fs.getPath(modulepath, module);
       
   265     }
       
   266 
       
   267     /**
       
   268      * Loads a class with the given file name from the specified {@link URLClassLoader}.
       
   269      */
       
   270     private Class<?> loadClassFile(final ClassLoader loader, final String fileName) {
       
   271         int start = 0;
       
   272         if (fileName.startsWith("/")) {
       
   273             start = 1;
       
   274         }
       
   275         String className = fileName.substring(start, fileName.length() - ".class".length());
       
   276         className = className.replace('/', '.');
       
   277         try {
       
   278             return loader.loadClass(className);
       
   279         } catch (Throwable e) {
       
   280             // If we are running in JCK mode we ignore all exceptions.
       
   281             if (options.ignoreClassLoadingErrors) {
       
   282                 log.printError(className + ": " + e);
       
   283                 return null;
       
   284             }
       
   285             throw new InternalError(e);
       
   286         }
       
   287     }
       
   288 
       
   289     /**
       
   290      * {@link FileVisitor} implementation to find class files recursively.
       
   291      */
       
   292     private static class FileSystemFinder extends SimpleFileVisitor<Path> {
       
   293         private final ArrayList<Path> fileNames = new ArrayList<>();
       
   294         private final PathMatcher filter;
       
   295 
       
   296         FileSystemFinder(Path combinedPath, PathMatcher filter) {
       
   297             this.filter = filter;
       
   298             try {
       
   299                 Files.walkFileTree(combinedPath, this);
       
   300             } catch (IOException e) {
       
   301                 throw new InternalError(e);
       
   302             }
       
   303         }
       
   304 
       
   305         /**
       
   306          * Compares the glob pattern against the file name.
       
   307          */
       
   308         void find(Path file) {
       
   309             Path name = file.getFileName();
       
   310             if (name != null && filter.matches(name)) {
       
   311                 fileNames.add(file);
       
   312             }
       
   313         }
       
   314 
       
   315         List<Path> done() {
       
   316             return fileNames;
       
   317         }
       
   318 
       
   319         @Override
       
   320         public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
       
   321             find(file);
       
   322             return CONTINUE;
       
   323         }
       
   324 
       
   325         @Override
       
   326         public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
       
   327             find(dir);
       
   328             return CONTINUE;
       
   329         }
       
   330 
       
   331     }
       
   332 }