8337221: CompileFramework: test library to conveniently compile java and jasm sources for fuzzing
Reviewed-by: chagedorn, tholenstein
This commit is contained in:
parent
724de68209
commit
b9b0bd0871
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.compile_framework;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Build a ClassLoader that loads from classpath and {@code classesDir}.
|
||||
* Helper class that generates a ClassLoader which allows loading classes
|
||||
* from the classpath (see {@link Utils#getClassPaths()}) and {@code classesDir}.
|
||||
* <p>
|
||||
* The CompileFramework compiles all its classes to a specific {@code classesDir},
|
||||
* and this generated ClassLoader thus can be used to load those classes.
|
||||
*/
|
||||
class ClassLoaderBuilder {
|
||||
|
||||
/**
|
||||
* Build a ClassLoader that loads from classpath and {@code classesDir}.
|
||||
*/
|
||||
public static ClassLoader build(Path classesDir) {
|
||||
ClassLoader sysLoader = ClassLoader.getSystemClassLoader();
|
||||
|
||||
try {
|
||||
// Classpath for all included classes (e.g. IR Framework).
|
||||
// Get all class paths, convert to URLs.
|
||||
List<URL> urls = new ArrayList<>();
|
||||
for (String path : Utils.getClassPaths()) {
|
||||
urls.add(new File(path).toURI().toURL());
|
||||
}
|
||||
// And add in the compiled classes from this instance of CompileFramework.
|
||||
urls.add(new File(classesDir.toString()).toURI().toURL());
|
||||
return URLClassLoader.newInstance(urls.toArray(URL[]::new), sysLoader);
|
||||
} catch (IOException e) {
|
||||
throw new CompileFrameworkException("IOException while creating ClassLoader", e);
|
||||
}
|
||||
}
|
||||
}
|
202
test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java
Normal file
202
test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java
Normal file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.compile_framework;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.List;
|
||||
import jdk.test.lib.JDKToolFinder;
|
||||
|
||||
/**
|
||||
* Helper class for compilation of Java and Jasm {@link SourceCode}.
|
||||
*/
|
||||
class Compile {
|
||||
private static final int COMPILE_TIMEOUT = 60;
|
||||
|
||||
private static final String JAVA_PATH = JDKToolFinder.getJDKTool("java");
|
||||
private static final String JAVAC_PATH = JDKToolFinder.getJDKTool("javac");
|
||||
|
||||
/**
|
||||
* Compile all sources in {@code javaSources}. First write them to the {@code sourceDir},
|
||||
* then compile them to class-files which are stored in {@code classesDir}.
|
||||
*/
|
||||
public static void compileJavaSources(List<SourceCode> javaSources, Path sourceDir, Path classesDir) {
|
||||
if (javaSources.isEmpty()) {
|
||||
Utils.printlnVerbose("No java sources to compile.");
|
||||
return;
|
||||
}
|
||||
Utils.printlnVerbose("Compiling Java sources: " + javaSources.size());
|
||||
|
||||
List<Path> javaFilePaths = writeSourcesToFiles(javaSources, sourceDir);
|
||||
compileJavaFiles(javaFilePaths, classesDir);
|
||||
Utils.printlnVerbose("Java sources compiled.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a list of files (i.e. {@code paths}) using javac and store
|
||||
* them in {@code classesDir}.
|
||||
*/
|
||||
private static void compileJavaFiles(List<Path> paths, Path classesDir) {
|
||||
List<String> command = new ArrayList<>();
|
||||
|
||||
command.add(JAVAC_PATH);
|
||||
command.add("-classpath");
|
||||
// Note: the backslashes from windows paths must be escaped!
|
||||
command.add(Utils.getEscapedClassPathAndClassesDir(classesDir));
|
||||
command.add("-d");
|
||||
command.add(classesDir.toString());
|
||||
for (Path path : paths) {
|
||||
command.add(path.toAbsolutePath().toString());
|
||||
}
|
||||
|
||||
executeCompileCommand(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile all sources in {@code jasmSources}. First write them to the {@code sourceDir},
|
||||
* then compile them to class-files which are stored in {@code classesDir}.
|
||||
*/
|
||||
public static void compileJasmSources(List<SourceCode> jasmSources, Path sourceDir, Path classesDir) {
|
||||
if (jasmSources.isEmpty()) {
|
||||
Utils.printlnVerbose("No jasm sources to compile.");
|
||||
return;
|
||||
}
|
||||
Utils.printlnVerbose("Compiling jasm sources: " + jasmSources.size());
|
||||
|
||||
List<Path> jasmFilePaths = writeSourcesToFiles(jasmSources, sourceDir);
|
||||
compileJasmFiles(jasmFilePaths, classesDir);
|
||||
Utils.printlnVerbose("Jasm sources compiled.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a list of files (i.e. {@code paths}) using asmtools jasm and store
|
||||
* them in {@code classesDir}.
|
||||
*/
|
||||
private static void compileJasmFiles(List<Path> paths, Path classesDir) {
|
||||
List<String> command = new ArrayList<>();
|
||||
|
||||
command.add(JAVA_PATH);
|
||||
command.add("-classpath");
|
||||
command.add(getAsmToolsPath());
|
||||
command.add("org.openjdk.asmtools.jasm.Main");
|
||||
command.add("-d");
|
||||
command.add(classesDir.toString());
|
||||
for (Path path : paths) {
|
||||
command.add(path.toAbsolutePath().toString());
|
||||
}
|
||||
|
||||
executeCompileCommand(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path of asmtools, which is shipped with JTREG.
|
||||
*/
|
||||
private static String getAsmToolsPath() {
|
||||
for (String path : Utils.getClassPaths()) {
|
||||
if (path.endsWith("jtreg.jar")) {
|
||||
File jtreg = new File(path);
|
||||
File dir = jtreg.getAbsoluteFile().getParentFile();
|
||||
File asmtools = new File(dir, "asmtools.jar");
|
||||
if (!asmtools.exists()) {
|
||||
throw new InternalCompileFrameworkException("Found jtreg.jar in classpath, but could not find asmtools.jar");
|
||||
}
|
||||
return asmtools.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
throw new InternalCompileFrameworkException("Could not find asmtools because could not find jtreg.jar in classpath");
|
||||
}
|
||||
|
||||
private static void writeCodeToFile(String code, Path path) {
|
||||
Utils.printlnVerbose("File: " + path);
|
||||
|
||||
// Ensure directory of the file exists.
|
||||
Path dir = path.getParent();
|
||||
try {
|
||||
Files.createDirectories(dir);
|
||||
} catch (Exception e) {
|
||||
throw new CompileFrameworkException("Could not create directory: " + dir, e);
|
||||
}
|
||||
|
||||
// Write to file.
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(path)) {
|
||||
writer.write(code);
|
||||
} catch (Exception e) {
|
||||
throw new CompileFrameworkException("Could not write file: " + path, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write each source in {@code sources} to a file inside {@code sourceDir}.
|
||||
*/
|
||||
private static List<Path> writeSourcesToFiles(List<SourceCode> sources, Path sourceDir) {
|
||||
List<Path> storedFiles = new ArrayList<>();
|
||||
for (SourceCode sourceCode : sources) {
|
||||
Path path = sourceDir.resolve(sourceCode.filePathName());
|
||||
writeCodeToFile(sourceCode.code(), path);
|
||||
storedFiles.add(path);
|
||||
}
|
||||
return storedFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a given compilation, given as a {@code command}.
|
||||
*/
|
||||
private static void executeCompileCommand(List<String> command) {
|
||||
Utils.printlnVerbose("Compile command: " + String.join(" ", command));
|
||||
|
||||
ProcessBuilder builder = new ProcessBuilder(command);
|
||||
builder.redirectErrorStream(true);
|
||||
|
||||
String output;
|
||||
int exitCode;
|
||||
try {
|
||||
Process process = builder.start();
|
||||
boolean exited = process.waitFor(COMPILE_TIMEOUT, TimeUnit.SECONDS);
|
||||
if (!exited) {
|
||||
process.destroyForcibly();
|
||||
System.out.println("Timeout: compile command: " + String.join(" ", command));
|
||||
throw new InternalCompileFrameworkException("Process timeout: compilation took too long.");
|
||||
}
|
||||
output = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
|
||||
exitCode = process.exitValue();
|
||||
} catch (IOException e) {
|
||||
throw new InternalCompileFrameworkException("IOException during compilation", e);
|
||||
} catch (InterruptedException e) {
|
||||
throw new CompileFrameworkException("InterruptedException during compilation", e);
|
||||
}
|
||||
|
||||
if (exitCode != 0 || !output.isEmpty()) {
|
||||
System.err.println("Compilation failed.");
|
||||
System.err.println("Exit code: " + exitCode);
|
||||
System.err.println("Output: '" + output + "'");
|
||||
throw new CompileFrameworkException("Compilation failed.");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.compile_framework;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is the entry-point for the Compile Framework. Its purpose it to allow
|
||||
* compilation and execution of Java and Jasm sources generated at runtime.
|
||||
*
|
||||
* <p> Please reference the README.md for more details and examples.
|
||||
*/
|
||||
public class CompileFramework {
|
||||
private final List<SourceCode> javaSources = new ArrayList<>();
|
||||
private final List<SourceCode> jasmSources = new ArrayList<>();
|
||||
private final Path sourceDir = Utils.makeUniqueDir("compile-framework-sources-");
|
||||
private final Path classesDir = Utils.makeUniqueDir("compile-framework-classes-");
|
||||
private ClassLoader classLoader;
|
||||
|
||||
/**
|
||||
* Set up a new Compile Framework instance, for a new compilation unit.
|
||||
*/
|
||||
public CompileFramework() {}
|
||||
|
||||
/**
|
||||
* Add a Java source to the compilation.
|
||||
*
|
||||
* @param className Class name of the class (e.g. "{@code p.xyz.YXZ}").
|
||||
* @param code Java code for the class, in the form of a {@link String}.
|
||||
*/
|
||||
public void addJavaSourceCode(String className, String code) {
|
||||
javaSources.add(new SourceCode(className, "java", code));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Jasm source to the compilation.
|
||||
*
|
||||
* @param className Class name of the class (e.g. "{@code p.xyz.YXZ}").
|
||||
* @param code Jasm code for the class, in the form of a {@link String}.
|
||||
*/
|
||||
public void addJasmSourceCode(String className, String code) {
|
||||
jasmSources.add(new SourceCode(className, "jasm", code));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile all sources: store the sources to the {@link sourceDir} directory, compile
|
||||
* Java and Jasm sources and store the generated class-files in the {@link classesDir}
|
||||
* directory.
|
||||
*/
|
||||
public void compile() {
|
||||
if (classLoader != null) {
|
||||
throw new CompileFrameworkException("Cannot compile twice!");
|
||||
}
|
||||
|
||||
Utils.printlnVerbose("------------------ CompileFramework: SourceCode -------------------");
|
||||
Utils.printlnVerbose(sourceCodesAsString(jasmSources));
|
||||
Utils.printlnVerbose(sourceCodesAsString(javaSources));
|
||||
|
||||
System.out.println("------------------ CompileFramework: Compilation ------------------");
|
||||
System.out.println("Source directory: " + sourceDir);
|
||||
System.out.println("Classes directory: " + classesDir);
|
||||
|
||||
Compile.compileJasmSources(jasmSources, sourceDir, classesDir);
|
||||
Compile.compileJavaSources(javaSources, sourceDir, classesDir);
|
||||
classLoader = ClassLoaderBuilder.build(classesDir);
|
||||
}
|
||||
|
||||
private static String sourceCodesAsString(List<SourceCode> sourceCodes) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (SourceCode sourceCode : sourceCodes) {
|
||||
builder.append("SourceCode: ").append(sourceCode.filePathName()).append(System.lineSeparator());
|
||||
builder.append(sourceCode.code()).append(System.lineSeparator());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Access a class from the compiled code.
|
||||
*
|
||||
* @param name Name of the class to be retrieved.
|
||||
* @return The class corresponding to the {@code name}.
|
||||
*/
|
||||
public Class<?> getClass(String name) {
|
||||
try {
|
||||
return Class.forName(name, true, classLoader);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new CompileFrameworkException("Class not found:", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke a static method from the compiled code.
|
||||
*
|
||||
* @param className Class name of a compiled class.
|
||||
* @param methodName Method name of the class.
|
||||
* @param args List of arguments for the method invocation.
|
||||
* @return Return value from the invocation.
|
||||
*/
|
||||
public Object invoke(String className, String methodName, Object[] args) {
|
||||
Method method = findMethod(className, methodName);
|
||||
|
||||
try {
|
||||
return method.invoke(null, args);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new CompileFrameworkException("Illegal access:", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new CompileFrameworkException("Invocation target:", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Method findMethod(String className, String methodName) {
|
||||
Class<?> c = getClass(className);
|
||||
Method[] methods = c.getDeclaredMethods();
|
||||
Method method = null;
|
||||
|
||||
for (Method m : methods) {
|
||||
if (m.getName().equals(methodName)) {
|
||||
if (method != null) {
|
||||
throw new CompileFrameworkException("Method name \"" + methodName + "\" not unique in class \n" + className + "\".");
|
||||
}
|
||||
method = m;
|
||||
}
|
||||
}
|
||||
|
||||
if (method == null) {
|
||||
throw new CompileFrameworkException("Method \"" + methodName + "\" not found in class \n" + className + "\".");
|
||||
}
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the classpath appended with the {@link classesDir}, where
|
||||
* the compiled classes are stored. This enables another VM to load
|
||||
* the compiled classes. Note, the string is already backslash escaped,
|
||||
* so that Windows paths which use backslashes can be used directly
|
||||
* as strings.
|
||||
*
|
||||
* @return Classpath appended with the path to the compiled classes.
|
||||
*/
|
||||
public String getEscapedClassPathOfCompiledClasses() {
|
||||
return Utils.getEscapedClassPathAndClassesDir(classesDir);
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.compile_framework;
|
||||
|
||||
/**
|
||||
* Exception thrown in the Compilation Framework. Most likely, the user is responsible for the failure.
|
||||
*/
|
||||
public class CompileFrameworkException extends RuntimeException {
|
||||
public CompileFrameworkException(String message) {
|
||||
super("Exception in Compile Framework:" + System.lineSeparator() + message);
|
||||
}
|
||||
|
||||
public CompileFrameworkException(String message, Throwable e) {
|
||||
super("Exception in Compile Framework:" + System.lineSeparator() + message, e);
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.compile_framework;
|
||||
|
||||
/**
|
||||
* Internal exception thrown in Compilation Framework. Most likely, this is due to a bug in the CompileFramework.
|
||||
*/
|
||||
public class InternalCompileFrameworkException extends RuntimeException {
|
||||
public InternalCompileFrameworkException(String message) {
|
||||
super("Internal exception in Compile Framework, please file a bug:" + System.lineSeparator() + message);
|
||||
}
|
||||
|
||||
public InternalCompileFrameworkException(String message, Throwable e) {
|
||||
super("Internal exception in Compile Framework, please file a bug:" + System.lineSeparator() + message, e);
|
||||
}
|
||||
}
|
57
test/hotspot/jtreg/compiler/lib/compile_framework/README.md
Normal file
57
test/hotspot/jtreg/compiler/lib/compile_framework/README.md
Normal file
@ -0,0 +1,57 @@
|
||||
# Compile Framework
|
||||
The Compile Framework allows the compilation and execution of Java and Jasm sources, which are generated at runtime.
|
||||
|
||||
## Motivation
|
||||
We want to be able to generate Java and Jasm source code in the form of Strings at runtime, then compile them, load the classes and invoke some methods. This allows us to write more elaborate tests. For example small dedicated fuzzers that are targetted at some specific compiler optimization.
|
||||
|
||||
This is more powerful than hand-written tests, as we can generalize tests and cover more examples. It can also be better than a script-generated test: those are static and often the script is not integrated with the generated test. Another limitation of a generator script is that it is only run once, creating fixed static tests. Compilation at runtime allows us to randomly generate tests each time.
|
||||
|
||||
Of course we could compile at runtime without this framework, but it abstracts away the complexity of compilation, and allows the test-writer to focus on the generation of the source code.
|
||||
|
||||
## How to Use the Compile Framework
|
||||
|
||||
Please reference the examples found in [examples](../../../testlibrary_tests/compile_framework/examples/). Some basic tests can be found in [tests](../../../testlibrary_tests/compile_framework/tests/).
|
||||
|
||||
Here a very simple example:
|
||||
|
||||
// Create a new CompileFramework instance.
|
||||
CompileFramework compileFramework = new CompileFramework();
|
||||
|
||||
// Add a java source file.
|
||||
compileFramework.addJavaSourceCode("XYZ", "<your XYZ definition string>");
|
||||
|
||||
// Compile the source file.
|
||||
compileFramework.compile();
|
||||
|
||||
// Object returnValue = XYZ.test(5);
|
||||
Object returnValue = compileFramework.invoke("XYZ", "test", new Object[] {5});
|
||||
|
||||
### Creating a new Compile Framework Instance
|
||||
|
||||
First, one must create a `new CompileFramework()`, which creates two directories: a sources and a classes directory (see `sourcesDir` and `classesDir` in [CompileFramework](./CompileFramework.java)). The sources directory is where all the sources are placed by the Compile Framework, and the classes directory is where all the compiled classes are placed by the Compile Framework.
|
||||
|
||||
The Compile Framework prints the names of the directories, they are subdirectories of the JTREG scratch directory `JTWork/scratch`.
|
||||
|
||||
### Adding Sources to the Compilation
|
||||
|
||||
Java and Jasm sources can be added to the compilation using `compileFramework.addJavaSourceCode()` and `compileFramework.addJasmSourceCode()`. The source classes can depend on each other, and they can also use the IR Framework ([IRFrameworkJavaExample](../../../testlibrary_tests/compile_framework/examples/IRFrameworkJavaExample.java)).
|
||||
|
||||
When using the IR Framework, or any other library that needs to be compiled, it can be necessary to explicitly let JTREG compile that library. For example with `@compile ../../../compiler/lib/ir_framework/TestFramework.java`. Otherwise, the corresponding class files may not be available, and a corresponding failure will be encounter at class loading.
|
||||
|
||||
### Compiling
|
||||
|
||||
All sources are compiled with `compileFramework.compile()`. First, the sources are stored to the sources directory, then compiled, and then the class-files stored in the classes directory. The respective directory names are printed, so that the user can easily access the generated files for debugging.
|
||||
|
||||
### Interacting with the Compiled Code
|
||||
|
||||
The compiled code is then loaded with a `ClassLoader`. The classes can be accessed directly with `compileFramework.getClass(name)`. Specific methods can also directly be invoked with `compileFramework.invoke()`.
|
||||
|
||||
Should one require the modified classpath that includes the compiled classes, this is available with `compileFramework.getEscapedClassPathOfCompiledClasses()`. This can be necessary if the test launches any other VMs that also access the compiled classes. This is for example necessary when using the IR Framework.
|
||||
|
||||
### Running the Compiled Code in a New VM
|
||||
|
||||
One can also run the compiled code in a new VM. For this, one has to set the classpath with `compileFramework.getEscapedClassPathOfCompiledClasses()` ([RunWithFlagsExample](../../../testlibrary_tests/compile_framework/examples/RunWithFlagsExample.java))
|
||||
|
||||
### Verbose Printing
|
||||
|
||||
For debugging purposes, one can enable verbose printing, with `-DCompileFrameworkVerbose=true`.
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.compile_framework;
|
||||
|
||||
/**
|
||||
* This class represents the source code of a specific class.
|
||||
*/
|
||||
record SourceCode(String className, String extension, String code) {
|
||||
public String filePathName() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(className.replace('.','/')).append(".").append(extension);
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
81
test/hotspot/jtreg/compiler/lib/compile_framework/Utils.java
Normal file
81
test/hotspot/jtreg/compiler/lib/compile_framework/Utils.java
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.compile_framework;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.Path;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utility class, with many helper methods for the Compile Framework.
|
||||
*/
|
||||
class Utils {
|
||||
private static final boolean VERBOSE = Boolean.getBoolean("CompileFrameworkVerbose");
|
||||
|
||||
/**
|
||||
* Verbose printing, enabled with {@code -DCompileFrameworkVerbose=true}.
|
||||
*/
|
||||
public static void printlnVerbose(String s) {
|
||||
if (VERBOSE) {
|
||||
System.out.println(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a temporary directory with a unique name to avoid collisions
|
||||
* with multi-threading. Used to create the sources and classes directories. Since they
|
||||
* are unique even across threads, the Compile Framework is multi-threading safe, i.e.
|
||||
* it does not have collisions if two instances generate classes with the same name.
|
||||
*/
|
||||
public static Path makeUniqueDir(String prefix) {
|
||||
try {
|
||||
return Files.createTempDirectory(Paths.get("."), prefix);
|
||||
} catch (Exception e) {
|
||||
throw new InternalCompileFrameworkException("Could not set up temporary directory", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all paths in the classpath.
|
||||
*/
|
||||
public static String[] getClassPaths() {
|
||||
String separator = File.pathSeparator;
|
||||
return System.getProperty("java.class.path").split(separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the classpath, appended with the {@code classesDir}.
|
||||
*/
|
||||
public static String getEscapedClassPathAndClassesDir(Path classesDir) {
|
||||
String cp = System.getProperty("java.class.path") +
|
||||
File.pathSeparator +
|
||||
classesDir.toAbsolutePath();
|
||||
// Escape the backslash for Windows paths. We are using the path in the
|
||||
// command-line and Java code, so we always want it to be escaped.
|
||||
return cp.replace("\\", "\\\\");
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Example test to use the Compile Framework.
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /test/lib /
|
||||
* @run driver compile_framework.examples.CombinedJavaJasmExample
|
||||
*/
|
||||
|
||||
package compile_framework.examples;
|
||||
|
||||
import compiler.lib.compile_framework.*;
|
||||
|
||||
/**
|
||||
* This test shows a compilation of multiple Java and Jasm source code files.
|
||||
* In this example, the classes even reference each other.
|
||||
*/
|
||||
public class CombinedJavaJasmExample {
|
||||
|
||||
// Generate a source jasm file as String
|
||||
public static String generateJasm() {
|
||||
return """
|
||||
package p/xyz;
|
||||
|
||||
super public class XYZJasm {
|
||||
public static Method test:"(I)I"
|
||||
stack 20 locals 20
|
||||
{
|
||||
iload_0;
|
||||
iconst_2;
|
||||
imul;
|
||||
invokestatic Method p/xyz/XYZJava."mul3":"(I)I";
|
||||
ireturn;
|
||||
}
|
||||
|
||||
public static Method mul5:"(I)I"
|
||||
stack 20 locals 20
|
||||
{
|
||||
iload_0;
|
||||
ldc 5;
|
||||
imul;
|
||||
ireturn;
|
||||
}
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
// Generate a source java file as String
|
||||
public static String generateJava() {
|
||||
return """
|
||||
package p.xyz;
|
||||
|
||||
public class XYZJava {
|
||||
public static int test(int i) {
|
||||
return p.xyz.XYZJasm.mul5(i * 7);
|
||||
}
|
||||
|
||||
public static int mul3(int i) {
|
||||
return i * 3;
|
||||
}
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Create a new CompileFramework instance.
|
||||
CompileFramework comp = new CompileFramework();
|
||||
|
||||
// Generate files.
|
||||
comp.addJasmSourceCode("p.xyz.XYZJasm", generateJasm());
|
||||
comp.addJavaSourceCode("p.xyz.XYZJava", generateJava());
|
||||
|
||||
// Compile the source files.
|
||||
comp.compile();
|
||||
|
||||
test(comp, "p.xyz.XYZJasm", "test", 11, 11 * 2 * 3);
|
||||
test(comp, "p.xyz.XYZJava", "test", 13, 13 * 7 * 5);
|
||||
|
||||
System.out.println("Success.");
|
||||
}
|
||||
|
||||
public static void test(CompileFramework comp, String className, String methodName, int input, int expected) {
|
||||
Object ret = comp.invoke(className, methodName, new Object[] {input});
|
||||
|
||||
// Extract return value of invocation, verify its value.
|
||||
int i = (int)ret;
|
||||
System.out.println("Result of call: " + i + " vs expected: " + expected);
|
||||
if (i != expected) {
|
||||
throw new RuntimeException("wrong value: " + i);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Example test to use the Compile Framework together with the IR Framework (i.e. TestFramework).
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /test/lib /
|
||||
* @compile ../../../compiler/lib/ir_framework/TestFramework.java
|
||||
* @run driver compile_framework.examples.IRFrameworkJavaExample
|
||||
*/
|
||||
|
||||
package compile_framework.examples;
|
||||
|
||||
import compiler.lib.compile_framework.*;
|
||||
import jdk.test.lib.Utils;
|
||||
import jdk.test.lib.Platform;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
/**
|
||||
* This test shows that the IR verification can be done on code compiled by the Compile Framework.
|
||||
* The "@compile" command for JTREG is required so that the IRFramework is compiled, other javac
|
||||
* might not compile it because it is not present in the class, only in the dynamically compiled
|
||||
* code.
|
||||
* <p>
|
||||
* Additionally, we must set the classpath for the Test-VM, so that it has access to all compiled
|
||||
* classes (see {@link CompileFramework#getEscapedClassPathOfCompiledClasses}).
|
||||
*/
|
||||
public class IRFrameworkJavaExample {
|
||||
|
||||
public static void main(String[] args) {
|
||||
testX1();
|
||||
testX2();
|
||||
}
|
||||
|
||||
// Generate a source java file as String
|
||||
public static String generateX1(CompileFramework comp) {
|
||||
return String.format("""
|
||||
import compiler.lib.ir_framework.*;
|
||||
|
||||
public class X1 {
|
||||
public static void main(String args[]) {
|
||||
TestFramework framework = new TestFramework(X1.class);
|
||||
framework.addFlags("-classpath", "%s");
|
||||
framework.start();
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.LOAD_VECTOR_F, "> 0"},
|
||||
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"})
|
||||
static float[] test() {
|
||||
float[] a = new float[1024*8];
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
a[i]++;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
""", comp.getEscapedClassPathOfCompiledClasses());
|
||||
}
|
||||
|
||||
static void testX1() {
|
||||
// Create a new CompileFramework instance.
|
||||
CompileFramework comp = new CompileFramework();
|
||||
|
||||
// Add a java source file.
|
||||
comp.addJavaSourceCode("X1", generateX1(comp));
|
||||
|
||||
// Compile the source file.
|
||||
comp.compile();
|
||||
|
||||
// X1.main();
|
||||
comp.invoke("X1", "main", new Object[] {null});
|
||||
}
|
||||
|
||||
// Generate a source java file as String
|
||||
public static String generateX2(CompileFramework comp) {
|
||||
// Example with conflicting "@IR" rules -> expect a IRViolationException.
|
||||
return String.format("""
|
||||
import compiler.lib.ir_framework.*;
|
||||
|
||||
public class X2 {
|
||||
public static void main(String args[]) {
|
||||
TestFramework framework = new TestFramework(X2.class);
|
||||
framework.addFlags("-classpath", "%s");
|
||||
framework.start();
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.LOAD, "> 0"})
|
||||
@IR(failOn = IRNode.LOAD)
|
||||
static void test() {
|
||||
}
|
||||
}
|
||||
""", comp.getEscapedClassPathOfCompiledClasses());
|
||||
}
|
||||
|
||||
static void testX2() {
|
||||
// Create a new CompileFramework instance.
|
||||
CompileFramework comp = new CompileFramework();
|
||||
|
||||
// Add a java source file.
|
||||
comp.addJavaSourceCode("X2", generateX2(comp));
|
||||
|
||||
// Compile the source file.
|
||||
comp.compile();
|
||||
|
||||
// Load the compiled class.
|
||||
Class<?> c = comp.getClass("X2");
|
||||
|
||||
// Invoke the "X2.main" method from the compiled and loaded class.
|
||||
try {
|
||||
c.getDeclaredMethod("main", new Class[] { String[].class }).invoke(null, new Object[] { null });
|
||||
|
||||
// Check if IR framework is expected to execute the IR rules.
|
||||
if (Utils.getTestJavaOpts().length == 0 && Platform.isDebugBuild() && !Platform.isInt() && !Platform.isComp()) {
|
||||
throw new RuntimeException("IRViolationException expected.");
|
||||
} else {
|
||||
System.out.println("Got no IRViolationException, but was also not expected.");
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException("No such method:", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Illegal access:", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Throwable t = e.getCause();
|
||||
if (t == null) {
|
||||
throw new RuntimeException("IRViolationException expected:", e);
|
||||
}
|
||||
if (!t.getClass().getSimpleName().equals("IRViolationException")) {
|
||||
throw new RuntimeException("IRViolationException expected:", e);
|
||||
}
|
||||
System.out.println("Success, we got a IRViolationException.");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Example test to use the Compile Framework.
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /test/lib /
|
||||
* @run driver comile_framework.examples.MultiFileJasmExample
|
||||
*/
|
||||
|
||||
package comile_framework.examples;
|
||||
|
||||
import compiler.lib.compile_framework.*;
|
||||
import java.io.StringWriter;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* This test shows a compilation of multiple jasm source code files.
|
||||
*/
|
||||
public class MultiFileJasmExample {
|
||||
|
||||
// Generate a source jasm file as String
|
||||
public static String generate(int i) {
|
||||
StringWriter writer = new StringWriter();
|
||||
PrintWriter out = new PrintWriter(writer);
|
||||
out.println("package p/xyz;");
|
||||
out.println("");
|
||||
out.println("super public class XYZ" + i + " {");
|
||||
out.println(" public static Method test:\"(I)I\"");
|
||||
out.println(" stack 20 locals 20");
|
||||
out.println(" {");
|
||||
out.println(" iload_0;");
|
||||
out.println(" iconst_2;"); // every call multiplies by 2, in total 2^10 = 1024
|
||||
out.println(" imul;");
|
||||
if (i != 0) {
|
||||
out.println(" invokestatic Method p/xyz/XYZ" + (i-1) + ".\"test\":\"(I)I\";");
|
||||
}
|
||||
out.println(" ireturn;");
|
||||
out.println(" }");
|
||||
out.println("}");
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Create a new CompileFramework instance.
|
||||
CompileFramework comp = new CompileFramework();
|
||||
|
||||
// Generate 10 files.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
comp.addJasmSourceCode("p.xyz.XYZ" + i, generate(i));
|
||||
}
|
||||
|
||||
// Compile the source files.
|
||||
comp.compile();
|
||||
|
||||
// Object ret = XYZ9.test(5);
|
||||
Object ret = comp.invoke("p.xyz.XYZ9", "test", new Object[] { 5 });
|
||||
|
||||
// Extract return value of invocation, verify its value.
|
||||
int i = (int)ret;
|
||||
System.out.println("Result of call: " + i);
|
||||
if (i != 5 * 1024) {
|
||||
throw new RuntimeException("wrong value: " + i);
|
||||
}
|
||||
System.out.println("Success.");
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Example test to use the Compile Framework.
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /test/lib /
|
||||
* @run driver compile_framework.examples.MultiFileJavaExample
|
||||
*/
|
||||
|
||||
package compile_framework.examples;
|
||||
|
||||
import compiler.lib.compile_framework.*;
|
||||
import java.io.StringWriter;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* This test shows a compilation of multiple java source code files.
|
||||
*/
|
||||
public class MultiFileJavaExample {
|
||||
|
||||
// Generate a source java file as String
|
||||
public static String generate(int i) {
|
||||
StringWriter writer = new StringWriter();
|
||||
PrintWriter out = new PrintWriter(writer);
|
||||
out.println("package p.xyz;");
|
||||
out.println("");
|
||||
out.println("public class XYZ" + i + " {");
|
||||
if (i > 0) {
|
||||
out.println(" public XYZ" + (i - 1) + " xyz = new XYZ" + (i - 1) + "();");
|
||||
}
|
||||
out.println("");
|
||||
out.println(" public static Object test() {");
|
||||
out.println(" return new XYZ" + i + "();");
|
||||
out.println(" }");
|
||||
out.println("}");
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Create a new CompileFramework instance.
|
||||
CompileFramework comp = new CompileFramework();
|
||||
|
||||
// Generate 10 files.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
comp.addJavaSourceCode("p.xyz.XYZ" + i, generate(i));
|
||||
}
|
||||
|
||||
// Compile the source files.
|
||||
comp.compile();
|
||||
|
||||
// Object ret = XYZ9.test();
|
||||
Object ret = comp.invoke("p.xyz.XYZ9", "test", new Object[] {});
|
||||
|
||||
if (!ret.getClass().getSimpleName().equals("XYZ9")) {
|
||||
throw new RuntimeException("wrong result:" + ret);
|
||||
}
|
||||
System.out.println("Success.");
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Example test to use the Compile Framework and run the compiled code with additional flags
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /test/lib /
|
||||
* @run driver compile_framework.examples.RunWithFlagsExample
|
||||
*/
|
||||
|
||||
package compile_framework.examples;
|
||||
|
||||
import compiler.lib.compile_framework.*;
|
||||
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
/**
|
||||
* This test shows how the generated code can be compiled and invoked in a new VM. This allows
|
||||
* the execution of the code with additional VM flags and options.
|
||||
* <p>
|
||||
* The new VM must be able to locate the class files of the newly compiled code. For this we
|
||||
* set the class path using {@link CompileFramework#getEscapedClassPathOfCompiledClasses}.
|
||||
*/
|
||||
public class RunWithFlagsExample {
|
||||
|
||||
private static String generate() {
|
||||
return """
|
||||
package p.xyz;
|
||||
|
||||
public class X {
|
||||
public static void main(String args[]) {
|
||||
System.out.println("Hello world!");
|
||||
System.out.println(System.getProperty("MyMessage", "fail"));
|
||||
System.err.println(args[0]);
|
||||
}
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Create a new CompileFramework instance.
|
||||
CompileFramework comp = new CompileFramework();
|
||||
|
||||
// Add a Java source file.
|
||||
comp.addJavaSourceCode("p.xyz.X", generate());
|
||||
|
||||
// Compile the source file.
|
||||
comp.compile();
|
||||
|
||||
// Build command line.
|
||||
String[] command = {
|
||||
// Set the classpath to include our newly compiled class.
|
||||
"-classpath",
|
||||
comp.getEscapedClassPathOfCompiledClasses(),
|
||||
// Pass additional flags here.
|
||||
// "-Xbatch" is a harmless VM flag, so this example runs everywhere without issues.
|
||||
"-Xbatch",
|
||||
// We can also pass properties like "MyMessage".
|
||||
"-DMyMessage=hello_world",
|
||||
"p.xyz.X",
|
||||
"hello_arg"
|
||||
};
|
||||
|
||||
// Execute the command, and capture the output.
|
||||
// The JTREG Java and VM options are automatically passed to the test VM.
|
||||
OutputAnalyzer analyzer = ProcessTools.executeTestJava(command);
|
||||
|
||||
// Verify output.
|
||||
analyzer.shouldHaveExitValue(0);
|
||||
analyzer.stdoutContains("Hello world!");
|
||||
analyzer.stdoutContains("hello_world");
|
||||
analyzer.stdoutContains("hello_arg");
|
||||
|
||||
// Print output to stderr.
|
||||
analyzer.reportDiagnosticSummary();
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Example test to use the Compile Framework.
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /test/lib /
|
||||
* @run driver compile_framework.examples.SimpleJasmExample
|
||||
*/
|
||||
|
||||
package compile_framework.examples;
|
||||
|
||||
import compiler.lib.compile_framework.*;
|
||||
|
||||
/**
|
||||
* This test shows a simple compilation of java source code, and its invocation.
|
||||
*/
|
||||
public class SimpleJasmExample {
|
||||
|
||||
// Generate a source jasm file as String
|
||||
public static String generate() {
|
||||
return """
|
||||
super public class XYZ {
|
||||
public static Method test:"(I)I"
|
||||
stack 20 locals 20
|
||||
{
|
||||
iload_0;
|
||||
iconst_2;
|
||||
imul;
|
||||
ireturn;
|
||||
}
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Create a new CompileFramework instance.
|
||||
CompileFramework comp = new CompileFramework();
|
||||
|
||||
// Add a java source file.
|
||||
String src = generate();
|
||||
comp.addJasmSourceCode("XYZ", src);
|
||||
|
||||
// Compile the source file.
|
||||
comp.compile();
|
||||
|
||||
// Object ret = XYZ.test(5);
|
||||
Object ret = comp.invoke("XYZ", "test", new Object[] {5});
|
||||
|
||||
// Extract return value of invocation, verify its value.
|
||||
int i = (int)ret;
|
||||
System.out.println("Result of call: " + i);
|
||||
if (i != 10) {
|
||||
throw new RuntimeException("wrong value: " + i);
|
||||
}
|
||||
System.out.println("Success.");
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Example test to use the Compile Framework.
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /test/lib /
|
||||
* @run driver compile_framework.examples.SimpleJavaExample
|
||||
*/
|
||||
|
||||
package compile_framework.examples;
|
||||
|
||||
import compiler.lib.compile_framework.*;
|
||||
|
||||
/**
|
||||
* This test shows a simple compilation of java source code, and its invocation.
|
||||
*/
|
||||
public class SimpleJavaExample {
|
||||
|
||||
// Generate a source java file as String
|
||||
public static String generate() {
|
||||
return """
|
||||
public class XYZ {
|
||||
public static int test(int i) {
|
||||
System.out.println("Hello from XYZ.test: " + i);
|
||||
return i * 2;
|
||||
}
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Create a new CompileFramework instance.
|
||||
CompileFramework comp = new CompileFramework();
|
||||
|
||||
// Add a java source file.
|
||||
comp.addJavaSourceCode("XYZ", generate());
|
||||
|
||||
// Compile the source file.
|
||||
comp.compile();
|
||||
|
||||
// Object ret = XYZ.test(5);
|
||||
Object ret = comp.invoke("XYZ", "test", new Object[] {5});
|
||||
|
||||
// Extract return value of invocation, verify its value.
|
||||
int i = (int)ret;
|
||||
System.out.println("Result of call: " + i);
|
||||
if (i != 10) {
|
||||
throw new RuntimeException("wrong value: " + i);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Example test with failing jasm compilation.
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /test/lib /
|
||||
* @run driver compile_framework.tests.TestBadJasmCompilation
|
||||
*/
|
||||
|
||||
package compile_framework.tests;
|
||||
|
||||
import compiler.lib.compile_framework.*;
|
||||
|
||||
public class TestBadJasmCompilation {
|
||||
|
||||
// Generate a source jasm file as String
|
||||
public static String generate() {
|
||||
return """
|
||||
super public class XYZ {
|
||||
some bad code
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Create a new CompileFramework instance.
|
||||
CompileFramework comp = new CompileFramework();
|
||||
|
||||
// Add a java source file.
|
||||
comp.addJasmSourceCode("XYZ", generate());
|
||||
|
||||
try {
|
||||
// Compile the source file.
|
||||
comp.compile();
|
||||
throw new RuntimeException("Expected compilation to fail.");
|
||||
} catch (CompileFrameworkException e) {
|
||||
System.out.println("Success, expected compilation to fail.");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Example test with failing java compilation.
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /test/lib /
|
||||
* @run driver compile_framework.tests.TestBadJavaCompilation
|
||||
*/
|
||||
|
||||
package compile_framework.tests;
|
||||
|
||||
import compiler.lib.compile_framework.*;
|
||||
|
||||
public class TestBadJavaCompilation {
|
||||
|
||||
// Generate a source java file as String
|
||||
public static String generate() {
|
||||
return """
|
||||
public class XYZ {
|
||||
some bad code
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Create a new CompileFramework instance.
|
||||
CompileFramework comp = new CompileFramework();
|
||||
|
||||
// Add a java source file.
|
||||
comp.addJavaSourceCode("XYZ", generate());
|
||||
|
||||
try {
|
||||
// Compile the source file.
|
||||
comp.compile();
|
||||
throw new RuntimeException("Expected compilation to fail.");
|
||||
} catch (CompileFrameworkException e) {
|
||||
System.out.println("Success, expected compilation to fail.");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Example test with multi-threaded use of the CompileFramework.
|
||||
* Tests that the source and class directories are set up correctly.
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /test/lib /
|
||||
* @run driver compile_framework.tests.TestConcurrentCompilation
|
||||
*/
|
||||
|
||||
package compile_framework.tests;
|
||||
|
||||
import compiler.lib.compile_framework.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TestConcurrentCompilation {
|
||||
|
||||
// Generate a source java file as String
|
||||
public static String generate(int i) {
|
||||
return String.format("""
|
||||
public class XYZ {
|
||||
public static int test() {
|
||||
return %d;
|
||||
}
|
||||
}
|
||||
""", i);
|
||||
}
|
||||
|
||||
public static void test(int i) {
|
||||
System.out.println("Generate and compile XYZ for " + i);
|
||||
CompileFramework comp = new CompileFramework();
|
||||
comp.addJavaSourceCode("XYZ", generate(i));
|
||||
comp.compile();
|
||||
|
||||
// Now, sleep to give the other threads time to compile and store their class-files.
|
||||
System.out.println("Sleep for " + i);
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
System.out.println("Sleep interrupted for " + i);
|
||||
}
|
||||
|
||||
// Now, hopefully all threads have compiled and stored their class-files.
|
||||
// We can check if we get the expected result, i.e. the class-file from the current thread.
|
||||
System.out.println("Run XYZ.test for " + i);
|
||||
int j = (int)comp.invoke("XYZ", "test", new Object[] {});
|
||||
if (i != j) {
|
||||
System.out.println("Wrong value: " + i + " vs " + j);
|
||||
throw new RuntimeException("Wrong value: " + i + " vs " + j);
|
||||
}
|
||||
System.out.println("Success for " + i);
|
||||
}
|
||||
|
||||
public static class MyRunnable implements Runnable {
|
||||
private int i;
|
||||
|
||||
public MyRunnable(int i) {
|
||||
this.i = i;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
TestConcurrentCompilation.test(i);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Generating threads:");
|
||||
List<Thread> threads = new ArrayList<Thread>();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Thread thread = new Thread(new MyRunnable(i));
|
||||
thread.start();
|
||||
threads.add(thread);
|
||||
}
|
||||
System.out.println("Waiting to join threads:");
|
||||
try {
|
||||
for (Thread thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("interrupted", e);
|
||||
}
|
||||
System.out.println("Success.");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user