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 jdk.internal.misc.Unsafe;
27
28 import java.nio.file.Files;
29 import java.nio.file.Path;
30 import java.nio.file.Paths;
31 import java.util.Objects;
32 import java.util.concurrent.Executor;
33 import java.util.concurrent.atomic.AtomicLong;
34 import java.util.regex.Matcher;
35 import java.util.regex.Pattern;
36
37 /**
38 * Abstract handler for path.
39 * Concrete subclasses should implement method {@link #process()}.
40 */
41 public abstract class PathHandler {
42 private static final Unsafe UNSAFE = Unsafe.getUnsafe();
43 private static final AtomicLong CLASS_COUNT = new AtomicLong(0L);
44 private static volatile boolean CLASSES_LIMIT_REACHED = false;
45 private static final Pattern JAR_IN_DIR_PATTERN
46 = Pattern.compile("^(.*[/\\\\])?\\*$");
47 protected final Path root;
48 protected final Executor executor;
49 private ClassLoader loader;
50
51 /**
52 * @param root root path to process
53 * @param executor executor used for process task invocation
54 * @throws NullPointerException if {@code root} or {@code executor} is
55 * {@code null}
56 */
57 protected PathHandler(Path root, Executor executor) {
58 Objects.requireNonNull(root);
59 Objects.requireNonNull(executor);
60 this.root = root.normalize();
61 this.executor = executor;
62 this.loader = ClassLoader.getSystemClassLoader();
63 }
64
65 /**
66 * Factory method. Construct concrete handler in depends from {@code path}.
67 *
68 * @param path the path to process
69 * @param executor executor used for compile task invocation
70 * @throws NullPointerException if {@code path} or {@code executor} is
71 * {@code null}
72 */
73 public static PathHandler create(String path, Executor executor) {
74 Objects.requireNonNull(path);
75 Objects.requireNonNull(executor);
76 Matcher matcher = JAR_IN_DIR_PATTERN.matcher(path);
77 if (matcher.matches()) {
78 path = matcher.group(1);
79 path = path.isEmpty() ? "." : path;
80 return new ClassPathJarInDirEntry(Paths.get(path), executor);
81 } else {
82 path = path.isEmpty() ? "." : path;
83 Path p = Paths.get(path);
84 if (isJarFile(p)) {
85 return new ClassPathJarEntry(p, executor);
86 } else if (isListFile(p)) {
87 return new ClassesListInFile(p, executor);
88 } else if (isJimageFile(p)) {
89 return new ClassPathJimageEntry(p, executor);
90 } else {
91 return new ClassPathDirEntry(p, executor);
92 }
93 }
94 }
95
96 private static boolean isJarFile(Path path) {
97 if (Files.isRegularFile(path)) {
98 String name = path.toString();
99 return Utils.endsWithIgnoreCase(name, ".zip")
100 || Utils.endsWithIgnoreCase(name, ".jar");
101 }
102 return false;
103 }
104
105 private static boolean isJimageFile(Path path) {
106 String filename = path.getFileName().toString();
107 return Files.isRegularFile(path)
108 && ("modules".equals(filename)
109 || Utils.endsWithIgnoreCase(filename, ".jimage"));
110 }
111
112 private static boolean isListFile(Path path) {
113 if (Files.isRegularFile(path)) {
114 String name = path.toString();
115 return Utils.endsWithIgnoreCase(name, ".lst");
116 }
117 return false;
118 }
119
120 /**
121 * Processes all classes in the specified path.
122 */
123 public abstract void process();
124
125 /**
126 * @return count of all classes in the specified path.
127 */
128 public abstract long classCount();
129
130 /**
131 * Sets class loader, that will be used to define class at
132 * {@link #processClass(String)}.
133 *
134 * @param loader class loader
135 * @throws NullPointerException if {@code loader} is {@code null}
136 */
137 protected final void setLoader(ClassLoader loader) {
138 Objects.requireNonNull(loader);
139 this.loader = loader;
140 }
141
142 /**
143 * Processes specified class.
144 * @param name fully qualified name of class to process
145 */
146 protected final void processClass(String name) {
147 Objects.requireNonNull(name);
148 if (CLASSES_LIMIT_REACHED) {
149 return;
150 }
151 long id = CLASS_COUNT.incrementAndGet();
152 if (id > Utils.COMPILE_THE_WORLD_STOP_AT) {
153 CLASSES_LIMIT_REACHED = true;
154 return;
155 }
156 if (id >= Utils.COMPILE_THE_WORLD_START_AT) {
157 try {
158 Class<?> aClass = loader.loadClass(name);
159 if (!"sun.reflect.misc.Trampoline".equals(name)
160 // workaround for JDK-8159155
161 && !"sun.tools.jconsole.OutputViewer".equals(name)) {
162 UNSAFE.ensureClassInitialized(aClass);
163 }
164 CompileTheWorld.OUT.printf("[%d]\t%s%n", id, name);
165 Compiler.compileClass(aClass, id, executor);
166 } catch (ClassNotFoundException e) {
167 CompileTheWorld.OUT.printf("Class %s loading failed : %s%n",
168 name, e.getMessage());
169 }
170 }
171 }
172
173 /**
174 * @return count of processed classes
175 */
176 public static long getProcessedClassCount() {
177 long id = CLASS_COUNT.get();
178 if (id < Utils.COMPILE_THE_WORLD_START_AT) {
179 return 0;
180 }
181 if (id > Utils.COMPILE_THE_WORLD_STOP_AT) {
182 return Utils.COMPILE_THE_WORLD_STOP_AT - Utils.COMPILE_THE_WORLD_START_AT + 1;
183 }
184 return id - Utils.COMPILE_THE_WORLD_START_AT + 1;
185 }
186
187 /**
188 * @return {@code true} if classes limit is reached and processing should be stopped
|
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 java.io.Closeable;
27 import java.nio.file.Files;
28 import java.nio.file.Path;
29 import java.nio.file.Paths;
30 import java.util.Collections;
31 import java.util.List;
32 import java.util.Objects;
33 import java.util.concurrent.Executor;
34 import java.util.concurrent.atomic.AtomicLong;
35 import java.util.function.Function;
36 import java.util.regex.Matcher;
37 import java.util.regex.Pattern;
38 import java.util.stream.Stream;
39
40 /**
41 * Handler for a path, responsible for processing classes in the path.
42 */
43 public class PathHandler implements Closeable {
44 public static abstract class PathEntry implements Closeable {
45 private final ClassLoader loader = new PathEntryClassLoader(this::findByteCode);
46
47 /**
48 * returns bytecode for the class
49 * @param name binary name of the class
50 * @return bytecode of the class or null if handler does not have any
51 * code for this name
52 */
53 protected abstract byte[] findByteCode(String name);
54
55 protected final Path root;
56
57 /**
58 * @param root path entry root
59 * @throws NullPointerException if {@code root} is {@code null}
60 */
61 protected PathEntry(Path root) {
62 Objects.requireNonNull(root, "root can not be null");
63 this.root = root.normalize();
64 }
65
66 /**
67 * @return classloader which will be used to define classes
68 */
69 protected final ClassLoader loader() {
70 return loader;
71 }
72
73 /**
74 * @return stream of all classes in the specified path.
75 */
76 protected abstract Stream<String> classes();
77
78 /**
79 * @return string description of the specific path.
80 */
81 protected abstract String description();
82
83 public void close() { }
84
85 }
86
87 private static class PathEntryClassLoader extends java.lang.ClassLoader {
88 private final Function<String, byte[]> findByteCode;
89
90 private PathEntryClassLoader(Function<String, byte[]> findByteCode) {
91 this.findByteCode = findByteCode;
92 }
93
94 @Override
95 protected Class<?> findClass(String name) throws ClassNotFoundException {
96 byte[] code = findByteCode.apply(name);
97 if (code == null) {
98 return super.findClass(name);
99 } else {
100 return defineClass(name, code, 0, code.length);
101 }
102 }
103 }
104
105 private static final AtomicLong CLASS_COUNT = new AtomicLong(0L);
106 private static volatile boolean CLASSES_LIMIT_REACHED = false;
107 private static final Pattern JAR_IN_DIR_PATTERN
108 = Pattern.compile("^(.*[/\\\\])?\\*$");
109
110 /**
111 * Factory method. Constructs list of handlers for {@code path}.
112 *
113 * @param path the path to process
114 * @throws NullPointerException if {@code path} or {@code executor} is
115 * {@code null}
116 */
117 public static List<PathHandler> create(String path) {
118 Objects.requireNonNull(path);
119 Matcher matcher = JAR_IN_DIR_PATTERN.matcher(path);
120 if (matcher.matches()) {
121 path = matcher.group(1);
122 path = path.isEmpty() ? "." : path;
123 return ClassPathJarInDirEntry.create(Paths.get(path));
124 } else {
125 path = path.isEmpty() ? "." : path;
126 Path p = Paths.get(path);
127 PathEntry entry;
128 if (isJarFile(p)) {
129 entry = new ClassPathJarEntry(p);
130 } else if (isListFile(p)) {
131 entry = new ClassesListInFile(p);
132 } else if (isJimageFile(p)) {
133 entry = new ClassPathJimageEntry(p);
134 } else {
135 entry = new ClassPathDirEntry(p);
136 }
137 return Collections.singletonList(new PathHandler(entry));
138 }
139 }
140
141 private static boolean isJarFile(Path path) {
142 if (Files.isRegularFile(path)) {
143 String name = path.toString();
144 return Utils.endsWithIgnoreCase(name, ".zip")
145 || Utils.endsWithIgnoreCase(name, ".jar");
146 }
147 return false;
148 }
149
150 private static boolean isJimageFile(Path path) {
151 String filename = path.getFileName().toString();
152 return Files.isRegularFile(path)
153 && ("modules".equals(filename)
154 || Utils.endsWithIgnoreCase(filename, ".jimage"));
155 }
156
157 private static boolean isListFile(Path path) {
158 if (Files.isRegularFile(path)) {
159 String name = path.toString();
160 return Utils.endsWithIgnoreCase(name, ".lst");
161 }
162 return false;
163 }
164
165 private final PathEntry entry;
166 protected PathHandler(PathEntry entry) {
167 Objects.requireNonNull(entry);
168 this.entry = entry;
169 }
170
171
172 @Override
173 public void close() {
174 entry.close();
175 }
176
177 /**
178 * Processes all classes in the specified path.
179 * @param executor executor used for process task invocation
180 */
181 public final void process(Executor executor) {
182 CompileTheWorld.OUT.println(entry.description());
183 entry.classes().forEach(s -> processClass(s, executor));
184 }
185
186 /**
187 * @return count of all classes in the specified path.
188 */
189 public long classCount() {
190 return entry.classes().count();
191 }
192
193
194 /**
195 * Processes specified class.
196 * @param name fully qualified name of class to process
197 */
198 protected final void processClass(String name, Executor executor) {
199 Objects.requireNonNull(name);
200 if (isFinished()) {
201 return;
202 }
203 long id = CLASS_COUNT.incrementAndGet();
204 if (id > Utils.COMPILE_THE_WORLD_STOP_AT) {
205 CLASSES_LIMIT_REACHED = true;
206 return;
207 }
208 if (id >= Utils.COMPILE_THE_WORLD_START_AT) {
209 Class<?> aClass;
210 Thread.currentThread().setContextClassLoader(entry.loader());
211 try {
212 CompileTheWorld.OUT.printf("[%d]\t%s%n", id, name);
213 aClass = entry.loader().loadClass(name);
214 Compiler.compileClass(aClass, id, executor);
215 } catch (Throwable e) {
216 CompileTheWorld.OUT.printf("[%d]\t%s\tWARNING skipped: %s%n",
217 id, name, e);
218 e.printStackTrace(CompileTheWorld.ERR);
219 }
220 }
221 }
222
223 /**
224 * @return count of processed classes
225 */
226 public static long getProcessedClassCount() {
227 long id = CLASS_COUNT.get();
228 if (id < Utils.COMPILE_THE_WORLD_START_AT) {
229 return 0;
230 }
231 if (id > Utils.COMPILE_THE_WORLD_STOP_AT) {
232 return Utils.COMPILE_THE_WORLD_STOP_AT - Utils.COMPILE_THE_WORLD_START_AT + 1;
233 }
234 return id - Utils.COMPILE_THE_WORLD_START_AT + 1;
235 }
236
237 /**
238 * @return {@code true} if classes limit is reached and processing should be stopped
|