1 /*
   2  * Copyright (c) 2013, 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 sun.hotspot.tools.ctw;
  25 
  26 import sun.hotspot.WhiteBox;
  27 import sun.misc.SharedSecrets;
  28 import sun.reflect.ConstantPool;
  29 
  30 import java.lang.reflect.Executable;
  31 
  32 import java.nio.file.Files;
  33 import java.nio.file.Path;
  34 import java.nio.file.Paths;
  35 import java.io.File;
  36 
  37 import java.util.Objects;
  38 import java.util.regex.Pattern;
  39 import java.util.regex.Matcher;
  40 import java.util.concurrent.Executor;
  41 import java.util.concurrent.atomic.AtomicLong;
  42 
  43 /**
  44  * Abstract handler for path. Provide method to compile whole class.
  45  * Also contains compiled methods and classes counters.
  46  * <p/>
  47  * Concrete subclasses should implement method {@link #process()}.
  48  *
  49  * @author igor.ignatyev@oracle.com
  50  */
  51 public abstract class PathHandler {
  52     private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
  53     private static final AtomicLong CLASS_COUNT = new AtomicLong(0L);
  54     private static final AtomicLong METHOD_COUNT = new AtomicLong(0L);
  55     private static final Pattern JAR_IN_DIR_PATTERN
  56             = Pattern.compile("^(.*[/\\\\])?\\*$");
  57     private static volatile boolean CLASSES_LIMIT_REACHED = false;
  58     protected final Path root;
  59     protected final Executor executor;
  60     private ClassLoader loader;
  61 
  62     /**
  63      * @param root     root path to process
  64      * @param executor executor used for process task invocation
  65      * @throws NullPointerException if {@code root} or {@code executor} is
  66      *                              {@code null}
  67      */
  68     protected PathHandler(Path root, Executor executor) {
  69         Objects.requireNonNull(root);
  70         Objects.requireNonNull(executor);
  71         this.root = root.normalize();
  72         this.executor = executor;
  73         this.loader = ClassLoader.getSystemClassLoader();
  74     }
  75 
  76     /**
  77      * @return count of processed classes
  78      */
  79     public static long getClassCount() {
  80         return CLASS_COUNT.get();
  81     }
  82 
  83     /**
  84      * @return count of processed methods
  85      */
  86     public static long getMethodCount() {
  87         return METHOD_COUNT.get();
  88     }
  89 
  90     /**
  91      * @return {@code true} if classes limit is reached
  92      */
  93     public static boolean isFinished() {
  94         return CLASSES_LIMIT_REACHED;
  95     }
  96 
  97     /**
  98      * Factory method. Construct concrete compiler in depends from
  99      * {@code path}.
 100      *
 101      * @param path     the path to process
 102      * @param executor executor used for compile task invocation
 103      * @throws NullPointerException if {@code path} or {@code executor} is
 104      *                              {@code null}
 105      */
 106     public static PathHandler create(String path, Executor executor) {
 107         Objects.requireNonNull(path);
 108         Objects.requireNonNull(executor);
 109         Matcher matcher = JAR_IN_DIR_PATTERN.matcher(path);
 110         if (matcher.matches()) {
 111             path = matcher.group(1);
 112             path = path.isEmpty() ? "." : path;
 113             return new ClassPathJarInDirEntry(Paths.get(path), executor);
 114         } else {
 115             path = path.isEmpty() ? "." : path;
 116             Path p = Paths.get(path);
 117             if (isJarFile(p)) {
 118                 return new ClassPathJarEntry(p, executor);
 119             } else if (isListFile(p)) {
 120                 return new ClassesListInFile(p, executor);
 121             } else {
 122                 return new ClassPathDirEntry(p, executor);
 123             }
 124         }
 125     }
 126 
 127     private static boolean isJarFile(Path path) {
 128         if (Files.isRegularFile(path)) {
 129             String name = path.toString();
 130             return Utils.endsWithIgnoreCase(name, ".zip")
 131                     || Utils.endsWithIgnoreCase(name, ".jar");
 132         }
 133         return false;
 134     }
 135 
 136     private static boolean isListFile(Path path) {
 137         if (Files.isRegularFile(path)) {
 138             String name = path.toString();
 139             return Utils.endsWithIgnoreCase(name, ".lst");
 140         }
 141         return false;
 142     }
 143 
 144     /**
 145      * Processes all classes in specified path.
 146      */
 147     public abstract void process();
 148 
 149     /**
 150      * Compiles all methods and constructors.
 151      *
 152      * @param name fully qualified name of class to compile
 153      */
 154     protected final void compileWholeClass(String name) {
 155         long id = CLASS_COUNT.incrementAndGet();
 156         if (id > Utils.COMPILE_THE_WORLD_STOP_AT) {
 157             CLASS_COUNT.decrementAndGet();
 158             CLASSES_LIMIT_REACHED = true;
 159             return;
 160         }
 161 
 162         try {
 163             Class aClass = Class.forName(name, true, loader);
 164             if (id >= Utils.COMPILE_THE_WORLD_START_AT) {
 165                 System.out.printf("[%d]\t%s%n", id, name);
 166                 ConstantPool constantPool = SharedSecrets.getJavaLangAccess().
 167                         getConstantPool(aClass);
 168                 if (Utils.COMPILE_THE_WORLD_PRELOAD_CLASSES) {
 169                     preloadClasses(name, id, constantPool);
 170                 }
 171                 long methodCount = 0;
 172                 for (Executable e : aClass.getDeclaredConstructors()) {
 173                     ++methodCount;
 174                     executor.execute(new CompileMethodCommand(id, name, e));
 175                 }
 176                 for (Executable e : aClass.getDeclaredMethods()) {
 177                     ++methodCount;
 178                     executor.execute(new CompileMethodCommand(id, name, e));
 179 
 180                 }
 181                 METHOD_COUNT.addAndGet(methodCount);
 182 
 183                 if (Utils.DEOPTIMIZE_ALL_CLASSES_RATE > 0
 184                         && (id % Utils.DEOPTIMIZE_ALL_CLASSES_RATE == 0)) {
 185                     WHITE_BOX.deoptimizeAll();
 186                 }
 187             }
 188 
 189         } catch (Throwable t) {
 190             System.out.printf("[%d]\t%s\tskipping %s%n", id, name, t);
 191             t.printStackTrace();
 192         }
 193     }
 194 
 195     private void preloadClasses(String className, long id,
 196             ConstantPool constantPool) {
 197         try {
 198             for (int i = 0, n = constantPool.getSize(); i < n; ++i) {
 199                 try {
 200                     constantPool.getClassAt(i);
 201                 } catch (IllegalArgumentException ignore) {
 202                 }
 203             }
 204         } catch (Throwable t) {
 205             System.out.printf("[%d]\t%s\tpreloading failed : %s%n", id,
 206                     className, t);
 207         }
 208     }
 209 
 210     /**
 211      * Sets class loader, that will be used to define class at
 212      * {@link #compileWholeClass(String)}.
 213      *
 214      * @param loader class loader
 215      * @throws NullPointerException if {@code loader} is {@code null}
 216      */
 217     protected final void setLoader(ClassLoader loader) {
 218         Objects.requireNonNull(loader);
 219         this.loader = loader;
 220     }
 221 }
 222