diff --git a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties index e65080fb077b9..fc7465b3b17ba 100644 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2023, 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 @@ -230,7 +230,7 @@ where possible options include:\n\ \ --help-extra, -X Print help on non-standard options and exit\n\ \n\ A file argument may be a file name, or one of the predefined file names: DEFAULT,\n\ -PRINTING, or JAVASE.\n\ +PRINTING, TOOLING, or JAVASE.\n\ A load-file may also be "-" to indicate standard input, without interactive I/O.\n\ \n\ For more information on the evaluation context options (--class-path,\n\ @@ -331,10 +331,10 @@ Open a file and read its contents as snippets and commands.\n\ Download and use the specified URL as the jshell tool input.\n\ \n\ The may be an operating system file name, or one of the predefined\n\ -file names: DEFAULT, PRINTING, or JAVASE.\n\ +file names: DEFAULT, PRINTING, TOOLING, or JAVASE.\n\ These are respectively: the default import snippets (as used by -default),\n\ -definitions of print(), println(), and printf() method snippets, or\n\ -imports of all Java SE packages.\n +definitions of print(), println(), and printf() method snippets, definitions\n\ +of method snippets running JDK tools, or imports of all Java SE packages.\n help.vars.summary = list the declared variables and their values help.vars.args = [|-all|-start] @@ -1136,7 +1136,7 @@ Note: if the startup was last set from a file, this is shown with the\n\ 'set start' command followed by the contents of the file.\n\ \n\ The may be an operating system file name, or one of the predefined\n\ -startup file names: DEFAULT, PRINTING, or JAVASE.\n\ +startup file names: DEFAULT, PRINTING, TOOLING, or JAVASE.\n\ These are respectively: the default import snippets (as used by -default),\n\ definitions of print(), println(), and printf() method snippets, or\n\ imports of all Java SE packages.\n\ diff --git a/src/jdk.jshell/share/classes/jdk/jshell/tool/resources/TOOLING.jsh b/src/jdk.jshell/share/classes/jdk/jshell/tool/resources/TOOLING.jsh new file mode 100644 index 0000000000000..b97b6341cfa02 --- /dev/null +++ b/src/jdk.jshell/share/classes/jdk/jshell/tool/resources/TOOLING.jsh @@ -0,0 +1,49 @@ +void jar(String... args) { run("jar", args); } +void javac(String... args) { run("javac", args); } +void javadoc(String... args) { run("javadoc", args); } +void javap(String... args) { run("javap", args); } +void jdeps(String... args) { run("jdeps", args); } +void jlink(String... args) { run("jlink", args); } +void jmod(String... args) { run("jmod", args); } +void jpackage(String... args) { run("jpackage", args); } + +void javap(Class type) throws Exception { + try { + var name = type.getCanonicalName(); + if (name == null) throw new IllegalArgumentException("Type not supported: " + type); + if (type == Class.forName(name, false, ClassLoader.getSystemClassLoader())) { + run("javap", "-c", "-v", "-s", name); + return; + } + } catch (ClassNotFoundException ignored) { + // fall-through + } + var temp = java.nio.file.Files.createTempFile("TOOLING-", ".class"); + try { + var name = type.getName().replace('.', '/') + ".class"; + try (var in = type.getClassLoader().getResourceAsStream(name); + var out = java.nio.file.Files.newOutputStream(temp)) { + if (in == null) throw new AssertionError("Resource not found: " + name); + in.transferTo(out); + } + run("javap", "-c", "-v", "-s", temp.toString()); + } finally { + java.nio.file.Files.delete(temp); + } +} + +void run(String name, String... args) { + var tool = java.util.spi.ToolProvider.findFirst(name); + if (tool.isEmpty()) throw new RuntimeException("No such tool found: " + name); + var code = tool.get().run(System.out, System.err, args); + if (code == 0) return; + System.err.println(name + " returned non-zero exit code: " + code); +} + +void tools() { + java.util.ServiceLoader.load(java.util.spi.ToolProvider.class).stream() + .map(java.util.ServiceLoader.Provider::get) + .map(java.util.spi.ToolProvider::name) + .sorted() + .forEach(System.out::println); +} diff --git a/test/langtools/jdk/jshell/ReplToolTesting.java b/test/langtools/jdk/jshell/ReplToolTesting.java index 3bec00350ffc5..09ee4117c1817 100644 --- a/test/langtools/jdk/jshell/ReplToolTesting.java +++ b/test/langtools/jdk/jshell/ReplToolTesting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, 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 @@ -510,6 +510,26 @@ public void assertCommandCheckOutput(boolean after, String cmd, Consumer } } + public void assertCommandUserOutputContains(boolean after, String cmd, String... hasThese) { + assertCommandCheckUserOutput(after, cmd, (s) + -> assertTrue(Arrays.stream(hasThese) + .allMatch(has -> s.contains(has)), + "User output: \'" + s + "' does not contain: " + + Arrays.stream(hasThese) + .filter(has -> !s.contains(has)) + .collect(Collectors.joining(", ")))); + } + + public void assertCommandCheckUserOutput(boolean after, String cmd, Consumer check) { + if (!after) { + assertCommand(false, cmd, null); + } else { + String got = getUserOutput(); + check.accept(got); + assertCommand(true, cmd, null); + } + } + public void assertCommand(boolean after, String cmd, String out, String err, String userinput, String print, String usererr) { if (!after) { diff --git a/test/langtools/jdk/jshell/ToolLocalSimpleTest.java b/test/langtools/jdk/jshell/ToolLocalSimpleTest.java index 25aed42030ea6..6221f29510360 100644 --- a/test/langtools/jdk/jshell/ToolLocalSimpleTest.java +++ b/test/langtools/jdk/jshell/ToolLocalSimpleTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, 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 @@ -71,9 +71,9 @@ public void testOptionR() { @Override @Test public void testCompoundStart() { - test(new String[]{"--startup", "DEFAULT", "--startup", "PRINTING"}, + test(new String[]{"--startup", "DEFAULT", "--startup", "PRINTING", "--startup", "TOOLING"}, (a) -> assertCommandOutputContains(a, "/list -start", - "System.out.println", "import java.util.concurrent") + "System.out.println", "import java.util.concurrent", "tools()") ); } diff --git a/test/langtools/jdk/jshell/ToolSimpleTest.java b/test/langtools/jdk/jshell/ToolSimpleTest.java index 052e55991de25..222acb5291c75 100644 --- a/test/langtools/jdk/jshell/ToolSimpleTest.java +++ b/test/langtools/jdk/jshell/ToolSimpleTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, 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 @@ -27,7 +27,7 @@ * 8167128 8154513 8170015 8170368 8172102 8172103 8165405 8173073 8173848 * 8174041 8173916 8174028 8174262 8174797 8177079 8180508 8177466 8172154 * 8192979 8191842 8198573 8198801 8210596 8210959 8215099 8199623 8236715 - * 8239536 8247456 8246774 8238173 8292625 + * 8239536 8247456 8246774 8238173 8292625 8306560 * @summary Simple jshell tool tests * @modules jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.main @@ -749,9 +749,11 @@ public void testBlankLinesInSnippetContinuation() { @Test public void testCompoundStart() { test(new String[]{"-R", "-Duser.language=en", "-R", "-Duser.country=US", - "--startup", "DEFAULT", "--startup", "PRINTING"}, + "--startup", "DEFAULT", "--startup", "PRINTING", "--startup", "TOOLING"}, (a) -> assertCommand(a, "printf(\"%4.2f\", Math.PI)", - "", "", null, "3.14", "") + "", "", null, "3.14", ""), + (a) -> assertCommand(a, "jar(\"--version\")", + "", "", null, "jar " + System.getProperty("java.version") + "\n", "") ); } diff --git a/test/langtools/jdk/jshell/ToolingTest.java b/test/langtools/jdk/jshell/ToolingTest.java new file mode 100644 index 0000000000000..b36fdc03c1900 --- /dev/null +++ b/test/langtools/jdk/jshell/ToolingTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023, 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 + * @bug 8306560 + * @summary Tests for snippets and methods defined in TOOLING.jsh + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.jdeps/com.sun.tools.javap + * jdk.jshell/jdk.internal.jshell.tool + * @build KullaTesting TestingInputStream + * @run testng ToolingTest + */ + +import org.testng.Assert; +import org.testng.annotations.Test; + +public class ToolingTest extends ReplToolTesting { + @Test + public void testListToolingSnippets() { + test( + a -> assertCommand(a, "/open TOOLING", + ""), + a -> assertCommandOutputContains(a, "/list", + // Tool methods + "void jar(String... args)", + // ... + "void jpackage(String... args)", + // Utility methods + "void javap(Class type) throws Exception", + "void run(String name, String... args)", + "void tools()") + ); + } + + @Test + public void testDisassembleJavaLangObjectClass() { + test( + a -> assertCommand(a, "/open TOOLING", + ""), + a -> assertCommandUserOutputContains(a, "javap(Object.class)", + "Classfile jrt:/java.base/java/lang/Object.class", + "SourceFile: \"Object.java\"") + ); + } + + @Test + public void testDisassembleNewRecordClass() { + test( + a -> assertCommand(a, "record Point(int x, int y) {}", + "| created record Point"), + a -> assertCommand(a, "/open TOOLING", + ""), + a -> assertCommandUserOutputContains(a, "javap(Point.class)", + "Classfile ", // Classfile /.../TOOLING-13366652659767559204.class + "Point extends java.lang.Record", // public final class REPL.$JShell$11$Point extends java.lang.Record + "SourceFile: \"$JShell$" // SourceFile: "$JShell$11.java" + ) + ); + } +}