Skip to content

Commit b9b0bd0

Browse files
committed
8337221: CompileFramework: test library to conveniently compile java and jasm sources for fuzzing
Reviewed-by: chagedorn, tholenstein
1 parent 724de68 commit b9b0bd0

18 files changed

+1604
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright (c) 2024, 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 compiler.lib.compile_framework;
25+
26+
import java.io.File;
27+
import java.io.IOException;
28+
import java.net.URL;
29+
import java.net.URLClassLoader;
30+
import java.nio.file.Path;
31+
import java.util.ArrayList;
32+
import java.util.List;
33+
34+
/**
35+
* Build a ClassLoader that loads from classpath and {@code classesDir}.
36+
* Helper class that generates a ClassLoader which allows loading classes
37+
* from the classpath (see {@link Utils#getClassPaths()}) and {@code classesDir}.
38+
* <p>
39+
* The CompileFramework compiles all its classes to a specific {@code classesDir},
40+
* and this generated ClassLoader thus can be used to load those classes.
41+
*/
42+
class ClassLoaderBuilder {
43+
44+
/**
45+
* Build a ClassLoader that loads from classpath and {@code classesDir}.
46+
*/
47+
public static ClassLoader build(Path classesDir) {
48+
ClassLoader sysLoader = ClassLoader.getSystemClassLoader();
49+
50+
try {
51+
// Classpath for all included classes (e.g. IR Framework).
52+
// Get all class paths, convert to URLs.
53+
List<URL> urls = new ArrayList<>();
54+
for (String path : Utils.getClassPaths()) {
55+
urls.add(new File(path).toURI().toURL());
56+
}
57+
// And add in the compiled classes from this instance of CompileFramework.
58+
urls.add(new File(classesDir.toString()).toURI().toURL());
59+
return URLClassLoader.newInstance(urls.toArray(URL[]::new), sysLoader);
60+
} catch (IOException e) {
61+
throw new CompileFrameworkException("IOException while creating ClassLoader", e);
62+
}
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
/*
2+
* Copyright (c) 2024, 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 compiler.lib.compile_framework;
25+
26+
import java.io.BufferedWriter;
27+
import java.io.File;
28+
import java.io.IOException;
29+
import java.nio.charset.StandardCharsets;
30+
import java.nio.file.Files;
31+
import java.nio.file.Path;
32+
import java.util.ArrayList;
33+
import java.util.concurrent.TimeUnit;
34+
import java.util.List;
35+
import jdk.test.lib.JDKToolFinder;
36+
37+
/**
38+
* Helper class for compilation of Java and Jasm {@link SourceCode}.
39+
*/
40+
class Compile {
41+
private static final int COMPILE_TIMEOUT = 60;
42+
43+
private static final String JAVA_PATH = JDKToolFinder.getJDKTool("java");
44+
private static final String JAVAC_PATH = JDKToolFinder.getJDKTool("javac");
45+
46+
/**
47+
* Compile all sources in {@code javaSources}. First write them to the {@code sourceDir},
48+
* then compile them to class-files which are stored in {@code classesDir}.
49+
*/
50+
public static void compileJavaSources(List<SourceCode> javaSources, Path sourceDir, Path classesDir) {
51+
if (javaSources.isEmpty()) {
52+
Utils.printlnVerbose("No java sources to compile.");
53+
return;
54+
}
55+
Utils.printlnVerbose("Compiling Java sources: " + javaSources.size());
56+
57+
List<Path> javaFilePaths = writeSourcesToFiles(javaSources, sourceDir);
58+
compileJavaFiles(javaFilePaths, classesDir);
59+
Utils.printlnVerbose("Java sources compiled.");
60+
}
61+
62+
/**
63+
* Compile a list of files (i.e. {@code paths}) using javac and store
64+
* them in {@code classesDir}.
65+
*/
66+
private static void compileJavaFiles(List<Path> paths, Path classesDir) {
67+
List<String> command = new ArrayList<>();
68+
69+
command.add(JAVAC_PATH);
70+
command.add("-classpath");
71+
// Note: the backslashes from windows paths must be escaped!
72+
command.add(Utils.getEscapedClassPathAndClassesDir(classesDir));
73+
command.add("-d");
74+
command.add(classesDir.toString());
75+
for (Path path : paths) {
76+
command.add(path.toAbsolutePath().toString());
77+
}
78+
79+
executeCompileCommand(command);
80+
}
81+
82+
/**
83+
* Compile all sources in {@code jasmSources}. First write them to the {@code sourceDir},
84+
* then compile them to class-files which are stored in {@code classesDir}.
85+
*/
86+
public static void compileJasmSources(List<SourceCode> jasmSources, Path sourceDir, Path classesDir) {
87+
if (jasmSources.isEmpty()) {
88+
Utils.printlnVerbose("No jasm sources to compile.");
89+
return;
90+
}
91+
Utils.printlnVerbose("Compiling jasm sources: " + jasmSources.size());
92+
93+
List<Path> jasmFilePaths = writeSourcesToFiles(jasmSources, sourceDir);
94+
compileJasmFiles(jasmFilePaths, classesDir);
95+
Utils.printlnVerbose("Jasm sources compiled.");
96+
}
97+
98+
/**
99+
* Compile a list of files (i.e. {@code paths}) using asmtools jasm and store
100+
* them in {@code classesDir}.
101+
*/
102+
private static void compileJasmFiles(List<Path> paths, Path classesDir) {
103+
List<String> command = new ArrayList<>();
104+
105+
command.add(JAVA_PATH);
106+
command.add("-classpath");
107+
command.add(getAsmToolsPath());
108+
command.add("org.openjdk.asmtools.jasm.Main");
109+
command.add("-d");
110+
command.add(classesDir.toString());
111+
for (Path path : paths) {
112+
command.add(path.toAbsolutePath().toString());
113+
}
114+
115+
executeCompileCommand(command);
116+
}
117+
118+
/**
119+
* Get the path of asmtools, which is shipped with JTREG.
120+
*/
121+
private static String getAsmToolsPath() {
122+
for (String path : Utils.getClassPaths()) {
123+
if (path.endsWith("jtreg.jar")) {
124+
File jtreg = new File(path);
125+
File dir = jtreg.getAbsoluteFile().getParentFile();
126+
File asmtools = new File(dir, "asmtools.jar");
127+
if (!asmtools.exists()) {
128+
throw new InternalCompileFrameworkException("Found jtreg.jar in classpath, but could not find asmtools.jar");
129+
}
130+
return asmtools.getAbsolutePath();
131+
}
132+
}
133+
throw new InternalCompileFrameworkException("Could not find asmtools because could not find jtreg.jar in classpath");
134+
}
135+
136+
private static void writeCodeToFile(String code, Path path) {
137+
Utils.printlnVerbose("File: " + path);
138+
139+
// Ensure directory of the file exists.
140+
Path dir = path.getParent();
141+
try {
142+
Files.createDirectories(dir);
143+
} catch (Exception e) {
144+
throw new CompileFrameworkException("Could not create directory: " + dir, e);
145+
}
146+
147+
// Write to file.
148+
try (BufferedWriter writer = Files.newBufferedWriter(path)) {
149+
writer.write(code);
150+
} catch (Exception e) {
151+
throw new CompileFrameworkException("Could not write file: " + path, e);
152+
}
153+
}
154+
155+
/**
156+
* Write each source in {@code sources} to a file inside {@code sourceDir}.
157+
*/
158+
private static List<Path> writeSourcesToFiles(List<SourceCode> sources, Path sourceDir) {
159+
List<Path> storedFiles = new ArrayList<>();
160+
for (SourceCode sourceCode : sources) {
161+
Path path = sourceDir.resolve(sourceCode.filePathName());
162+
writeCodeToFile(sourceCode.code(), path);
163+
storedFiles.add(path);
164+
}
165+
return storedFiles;
166+
}
167+
168+
/**
169+
* Execute a given compilation, given as a {@code command}.
170+
*/
171+
private static void executeCompileCommand(List<String> command) {
172+
Utils.printlnVerbose("Compile command: " + String.join(" ", command));
173+
174+
ProcessBuilder builder = new ProcessBuilder(command);
175+
builder.redirectErrorStream(true);
176+
177+
String output;
178+
int exitCode;
179+
try {
180+
Process process = builder.start();
181+
boolean exited = process.waitFor(COMPILE_TIMEOUT, TimeUnit.SECONDS);
182+
if (!exited) {
183+
process.destroyForcibly();
184+
System.out.println("Timeout: compile command: " + String.join(" ", command));
185+
throw new InternalCompileFrameworkException("Process timeout: compilation took too long.");
186+
}
187+
output = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
188+
exitCode = process.exitValue();
189+
} catch (IOException e) {
190+
throw new InternalCompileFrameworkException("IOException during compilation", e);
191+
} catch (InterruptedException e) {
192+
throw new CompileFrameworkException("InterruptedException during compilation", e);
193+
}
194+
195+
if (exitCode != 0 || !output.isEmpty()) {
196+
System.err.println("Compilation failed.");
197+
System.err.println("Exit code: " + exitCode);
198+
System.err.println("Output: '" + output + "'");
199+
throw new CompileFrameworkException("Compilation failed.");
200+
}
201+
}
202+
}

0 commit comments

Comments
 (0)