| /* | |
| * Copyright (c) 2025, 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. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * 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.io.ByteArrayOutputStream; | |
| import java.io.File; | |
| import java.io.PrintStream; | |
| import java.nio.charset.StandardCharsets; | |
| import java.util.Arrays; | |
| import java.util.List; | |
| import java.util.Locale; | |
| import java.util.logging.Level; | |
| import java.util.logging.Logger; | |
| import java.util.regex.Matcher; | |
| import java.util.regex.Pattern; | |
| import java.util.spi.ToolProvider; | |
| import java.util.stream.Stream; | |
| import static java.util.stream.Collectors.*; | |
| // This program is executed by make/RunTests.gmk to support running HotSpot tests | |
| // in the "AOT mode", for example: | |
| // | |
| // make test JTREG=AOT_JDK=onestep TEST=open/test/hotspot/jtreg/runtime/invokedynamic | |
| // make test JTREG=AOT_JDK=twostep TEST=open/test/hotspot/jtreg/runtime/invokedynamic | |
| // | |
| // The onestep and twostep options specify whether the AOT cache is created with | |
| // a single JVM command (java -XX:AOTMode=record -XX:AOTCacheOutput=jdk.aotcache ...) or | |
| // two JVM commands (java -XX:AOTMode=record ...; java -XX:AOTMode=create -XX:AOTCache=jdk.aotcache ...) | |
| // | |
| // All JDK classes touched by this program will be stored into a customized AOT cache. | |
| // This is a larger set of classes than those stored in the JDK's default CDS archive. | |
| // This customized cache can also have additional optimizations that are not | |
| // enabled in the default CDS archive. For example, AOT-linked classes and lambda | |
| // expressions. In the future, it can also contain AOT profiles and AOT compiled methods. | |
| // | |
| // We can use this customized AOT cache to run various HotSpot tests to improve | |
| // coverage on AOT. | |
| // | |
| // Note that make/RunTests.gmk loads this class using an implicit classpath of ".", so | |
| // this class will be excluded from the customized AOT cache. As a result, | |
| // the customized AOT cache contains *only* classes from the JDK itself. | |
| public class TestSetupAOT { | |
| private static final Logger LOGGER = Logger.getLogger("Hello"); | |
| public static void main(String[] args) throws Throwable { | |
| runJDKTools(args); | |
| invokedynamicTests(args); | |
| LOGGER.log(Level.FINE, "Done"); | |
| } | |
| static class ToolOutput { | |
| ByteArrayOutputStream baos; | |
| PrintStream ps; | |
| String output; | |
| ToolOutput() throws Exception { | |
| baos = new ByteArrayOutputStream(); | |
| ps = new PrintStream(baos, true, StandardCharsets.UTF_8.name()); | |
| } | |
| void finish() throws Exception { | |
| output = baos.toString(StandardCharsets.UTF_8.name()); | |
| System.out.println(output); | |
| } | |
| ToolOutput shouldContain(String... substrings) { | |
| for (String s : substrings) { | |
| if (!output.contains(s)) { | |
| throw new RuntimeException("\"" + s + "\" missing from tool output"); | |
| } | |
| } | |
| return this; | |
| } | |
| ToolOutput shouldMatch(String... regexps) { | |
| for (String regexp : regexps) { | |
| Pattern pattern = Pattern.compile(regexp, Pattern.MULTILINE); | |
| if (!pattern.matcher(output).find()) { | |
| throw new RuntimeException("Pattern \"" + regexp + "\" missing from tool output"); | |
| } | |
| } | |
| return this; | |
| } | |
| } | |
| static void runJDKTools(String[] args) throws Throwable { | |
| String tmpDir = args[0]; | |
| System.out.println("Working Directory = " + System.getProperty("user.dir")); | |
| System.out.println("Temp output dir = " + tmpDir); | |
| // ------------------------------ | |
| // javac | |
| execTool("javac", "--help") | |
| .shouldContain("Usage: javac <options> <source files>"); | |
| JavacBenchApp.main(new String[] {"5"}); | |
| // ------------------------------ | |
| // javap | |
| execTool("javap", "--help") | |
| .shouldContain("Show package/protected/public classes"); | |
| execTool("javap", "-c", "-private", "-v", "-verify", | |
| "java.lang.System", | |
| "java/util/stream/IntStream", | |
| "jdk.internal.module.ModuleBootstrap") | |
| .shouldContain("Compiled from \"System.java\"", | |
| "public static java.io.Console console()"); | |
| // ------------------------------ | |
| // jlink | |
| String jlinkOutput = tmpDir + File.separator + "jlinkOutput"; | |
| execTool("jlink", "--help") | |
| .shouldContain("Compression to use in compressing resources"); | |
| execTool("jlink", "--list-plugins") | |
| .shouldContain("List of available plugins", | |
| "--generate-cds-archive "); | |
| deleteAll(jlinkOutput); | |
| execTool("jlink", "--add-modules", "java.base", "--strip-debug", "--output", jlinkOutput); | |
| deleteAll(jlinkOutput); | |
| // ------------------------------ | |
| // jar | |
| String jarOutput = tmpDir + File.separator + "tmp.jar"; | |
| execTool("jar", "--help") | |
| .shouldContain("--main-class=CLASSNAME"); | |
| deleteAll(jarOutput); | |
| execTool("jar", "cvf", jarOutput, "TestSetupAOT.class") | |
| .shouldContain("adding: TestSetupAOT.class"); | |
| execTool("jar", "uvf", jarOutput, "TestSetupAOT.class") | |
| .shouldContain("adding: TestSetupAOT.class"); | |
| execTool("jar", "tvf", jarOutput) | |
| .shouldContain("META-INF/MANIFEST.MF"); | |
| execTool("jar", "--describe-module", "--file=" + jarOutput) | |
| .shouldMatch("Unable to derive module descriptor for: .*tmp.jar"); | |
| deleteAll(jarOutput); | |
| // ------------------------------ | |
| // jdeps | |
| execTool("jdeps", "--help") | |
| .shouldContain("--ignore-missing-deps"); | |
| execTool("jdeps", "-v", "TestSetupAOT.class") | |
| .shouldContain("-> JavacBenchApp"); | |
| } | |
| static void deleteAll(String f) { | |
| deleteAll(new File(f)); | |
| } | |
| static void deleteAll(File f) { | |
| File[] files = f.listFiles(); | |
| if (files != null) { | |
| for (File file : files) { | |
| deleteAll(file); | |
| } | |
| } | |
| System.out.println("Deleting: " + f); | |
| f.delete(); | |
| } | |
| static ToolOutput execTool(String tool, String... args) throws Throwable { | |
| System.out.println("== Running tool ======================================================"); | |
| System.out.print(tool); | |
| for (String s : args) { | |
| System.out.print(" " + s); | |
| } | |
| System.out.println(); | |
| System.out.println("======================================================================"); | |
| ToolOutput output = new ToolOutput(); | |
| ToolProvider t = ToolProvider.findFirst(tool) | |
| .orElseThrow(() -> new RuntimeException(tool + " not found")); | |
| t.run(output.ps, output.ps, args); | |
| output.finish(); | |
| return output; | |
| } | |
| // Run some operations with java.util.stream, lambda expressions and string concatenation. This | |
| // will lead to AOT resolution of invokedynamic call sites. | |
| static void invokedynamicTests(String args[]) { | |
| List<String> strings = Arrays.asList("Hello", "World!"); | |
| String helloWorld = strings.parallelStream() | |
| .filter(s -> s.contains("o")) | |
| .map(s -> s.toLowerCase(Locale.ROOT)) | |
| .collect(joining(",")); | |
| Stream.of(helloWorld.split("([,x-z]{1,3})([\\s]*)")) | |
| .map(String::toString) | |
| .forEach(System.out::println); | |
| // Common concatenation patterns | |
| int i = args.length * 12357; // Seed with this so javac will not perform constant folding. | |
| String s = String.valueOf(i); | |
| String SS = s + s; | |
| String CS = "string" + s; | |
| String SC = s + "string"; | |
| String SCS = s + "string" + s; | |
| String CSS = "string" + s + s; | |
| String CSC = "string" + s + "string"; | |
| String SSC = s + s + "string"; | |
| String CSCS = "string" + s + "string" + s; | |
| String SCSC = s + "string" + s + "string"; | |
| String CSCSC = "string" + s + "string" + s + "string"; | |
| String SCSCS = s + "string" + s + "string" + s; | |
| String SSCSS = s + s + "string" + s + s; | |
| String S5 = s + s + s + s + s; | |
| String S6 = s + s + s + s + s + s; | |
| String S7 = s + s + s + s + s + s + s; | |
| String S8 = s + s + s + s + s + s + s + s; | |
| String S9 = s + s + s + s + s + s + s + s + s; | |
| String S10 = s + s + s + s + s + s + s + s + s + s; | |
| String CI = "string" + i; | |
| String IC = i + "string"; | |
| String SI = s + i; | |
| String IS = i + s; | |
| String CIS = "string" + i + s; | |
| String CSCI = "string" + s + "string" + i; | |
| String CIC = "string" + i + "string"; | |
| String CICI = "string" + i + "string" + i; | |
| float f = 0.1f; | |
| String CF = "string" + f; | |
| String CFS = "string" + f + s; | |
| String CSCF = "string" + s + "string" + f; | |
| char c = 'a'; | |
| String CC = "string" + c; | |
| String CCS = "string" + c + s; | |
| String CSCC = "string" + s + "string" + c; | |
| long l = i + 12345678; | |
| String CJ = "string" + l; | |
| String JC = l + "string"; | |
| String CJC = "string" + l + "string"; | |
| String CJCJ = "string" + l + "string" + l; | |
| String CJCJC = "string" + l + "string" + l + "string"; | |
| double d = i / 2.0; | |
| String CD = "string" + d; | |
| String CDS = "string" + d + s; | |
| String CSCD = "string" + s + "string" + d; | |
| } | |
| } |