7fd9d6c760
Reviewed-by: ccheung, matsaave
229 lines
8.1 KiB
Java
229 lines
8.1 KiB
Java
/*
|
|
* Copyright (c) 2023, 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.
|
|
*
|
|
*/
|
|
|
|
import java.lang.invoke.MethodHandles;
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.IOException;
|
|
import java.net.URI;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.concurrent.Callable;
|
|
import javax.tools.Diagnostic;
|
|
import javax.tools.DiagnosticCollector;
|
|
import javax.tools.FileObject;
|
|
import javax.tools.ForwardingJavaFileManager;
|
|
import javax.tools.JavaCompiler;
|
|
import javax.tools.JavaFileManager;
|
|
import javax.tools.JavaFileObject;
|
|
import javax.tools.SimpleJavaFileObject;
|
|
import javax.tools.ToolProvider;
|
|
|
|
/**
|
|
* This program tries to compile a large number of classes that exercise a fair amount of
|
|
* features in javac.
|
|
*/
|
|
public class JavacBenchApp {
|
|
static class ClassFile extends SimpleJavaFileObject {
|
|
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
protected ClassFile(String name) {
|
|
super(URI.create("memo:///" + name.replace('.', '/') + Kind.CLASS.extension), Kind.CLASS);
|
|
}
|
|
@Override
|
|
public ByteArrayOutputStream openOutputStream() {
|
|
return this.baos;
|
|
}
|
|
byte[] toByteArray() {
|
|
return baos.toByteArray();
|
|
}
|
|
}
|
|
|
|
static class FileManager extends ForwardingJavaFileManager<JavaFileManager> {
|
|
private Map<String, ClassFile> classesMap = new HashMap<String, ClassFile>();
|
|
protected FileManager(JavaFileManager fileManager) {
|
|
super(fileManager);
|
|
}
|
|
@Override
|
|
public ClassFile getJavaFileForOutput(Location location, String name, JavaFileObject.Kind kind, FileObject source) {
|
|
ClassFile classFile = new ClassFile(name);
|
|
classesMap.put(name, classFile);
|
|
return classFile;
|
|
}
|
|
public Map<String, byte[]> getCompiledClasses() {
|
|
Map<String, byte[]> result = new HashMap<>();
|
|
for (Map.Entry<String, ClassFile> entry : classesMap.entrySet()) {
|
|
result.put(entry.getKey(), entry.getValue().toByteArray());
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
static class SourceFile extends SimpleJavaFileObject {
|
|
private CharSequence sourceCode;
|
|
public SourceFile(String name, CharSequence sourceCode) {
|
|
super(URI.create("memo:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
|
|
this.sourceCode = sourceCode;
|
|
}
|
|
@Override
|
|
public CharSequence getCharContent(boolean ignore) {
|
|
return this.sourceCode;
|
|
}
|
|
}
|
|
|
|
public Map<String, byte[]> compile() {
|
|
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
|
DiagnosticCollector<JavaFileObject> ds = new DiagnosticCollector<>();
|
|
Collection<SourceFile> sourceFiles = sources;
|
|
|
|
try (FileManager fileManager = new FileManager(compiler.getStandardFileManager(ds, null, null))) {
|
|
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, sourceFiles);
|
|
if (task.call()) {
|
|
return fileManager.getCompiledClasses();
|
|
} else {
|
|
for (Diagnostic<? extends JavaFileObject> d : ds.getDiagnostics()) {
|
|
System.out.format("Line: %d, %s in %s", d.getLineNumber(), d.getMessage(null), d.getSource().getName());
|
|
}
|
|
throw new InternalError("compilation failure");
|
|
}
|
|
} catch (IOException e) {
|
|
throw new InternalError(e);
|
|
}
|
|
}
|
|
|
|
List<SourceFile> sources;
|
|
|
|
static final String imports = """
|
|
import java.lang.*;
|
|
import java.util.*;
|
|
""";
|
|
|
|
static final String testClassBody = """
|
|
// Some comments
|
|
static long x;
|
|
static final long y;
|
|
static {
|
|
y = System.currentTimeMillis();
|
|
}
|
|
/* More comments */
|
|
@Deprecated
|
|
String func() { return "String " + this + y; }
|
|
public static void main(String args[]) {
|
|
try {
|
|
x = Long.parseLong(args[0]);
|
|
} catch (Throwable t) {
|
|
t.printStackTrace();
|
|
}
|
|
doit(() -> {
|
|
System.out.println("Hello Lambda");
|
|
Thread.dumpStack();
|
|
});
|
|
}
|
|
static List<String> list = List.of("1", "2");
|
|
class InnerClass1 {
|
|
static final long yy = y;
|
|
}
|
|
static void doit(Runnable r) {
|
|
for (var x : list) {
|
|
r.run();
|
|
}
|
|
}
|
|
static String patternMatch(String arg, Object o) {
|
|
if (o instanceof String s) {
|
|
return "1234";
|
|
}
|
|
final String b = "B";
|
|
return switch (arg) {
|
|
case "A" -> "a";
|
|
case b -> "b";
|
|
default -> "c";
|
|
};
|
|
}
|
|
public sealed class SealedInnerClass {}
|
|
public final class Foo extends SealedInnerClass {}
|
|
enum Expression {
|
|
ADDITION,
|
|
SUBTRACTION,
|
|
MULTIPLICATION,
|
|
DIVISION
|
|
}
|
|
public record Point(int x, int y) {
|
|
public Point(int x) {
|
|
this(x, 0);
|
|
}
|
|
}
|
|
""";
|
|
|
|
String sanitySource = """
|
|
public class Sanity implements java.util.concurrent.Callable<String> {
|
|
public String call() {
|
|
return "this is a test";
|
|
}
|
|
}
|
|
""";
|
|
|
|
void setup(int count) {
|
|
sources = new ArrayList<>(count);
|
|
for (int i = 0; i < count; i++) {
|
|
String source = imports + "public class Test" + i + " {" + testClassBody + "}";
|
|
sources.add(new SourceFile("Test" + i, source));
|
|
}
|
|
|
|
sources.add(new SourceFile("Sanity", sanitySource));
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
static void validate(byte[] sanityClassFile) throws Throwable {
|
|
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
|
Class<?> cls = lookup.defineClass(sanityClassFile);
|
|
Callable<String> obj = (Callable<String>)cls.getDeclaredConstructor().newInstance();
|
|
String s = obj.call();
|
|
if (!s.equals("this is a test")) {
|
|
throw new RuntimeException("Expected \"this is a test\", but got \"" + s + "\"");
|
|
}
|
|
}
|
|
|
|
public static void main(String args[]) throws Throwable {
|
|
long started = System.currentTimeMillis();
|
|
JavacBenchApp bench = new JavacBenchApp();
|
|
|
|
int count = 0;
|
|
if (args.length > 0) {
|
|
count = Integer.parseInt(args[0]);
|
|
if (count >= 0) {
|
|
bench.setup(count);
|
|
Map<String, byte[]> allClasses = bench.compile();
|
|
validate(allClasses.get("Sanity"));
|
|
}
|
|
}
|
|
if (System.getProperty("JavacBenchApp.silent") == null) {
|
|
// Set this property when running with "perf stat", etc
|
|
long elapsed = System.currentTimeMillis() - started;
|
|
System.out.println("Generated source code for " + bench.sources.size() + " classes and compiled them in " + elapsed + " ms");
|
|
}
|
|
}
|
|
}
|
|
|