From 2dd73179c82b9c7f09945813c887fba671935272 Mon Sep 17 00:00:00 2001 From: KIRIYAMA Takuya Date: Thu, 28 Nov 2024 11:40:15 +0000 Subject: [PATCH 01/11] 8333427: langtools/tools/javac/newlines/NewLineTest.java is failing on Japanese Windows Backport-of: 1b0281dc77f41fc5df323c7f7b25a4138b1ffb9e --- test/langtools/tools/javac/newlines/NewLineTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/langtools/tools/javac/newlines/NewLineTest.java b/test/langtools/tools/javac/newlines/NewLineTest.java index b1567d363b0..35c60e59653 100644 --- a/test/langtools/tools/javac/newlines/NewLineTest.java +++ b/test/langtools/tools/javac/newlines/NewLineTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -58,8 +58,9 @@ public static void main(String args[]) throws Exception { .options("-J-Dline.separator='@'") .run(Task.Expect.FAIL); - List lines = Files.readAllLines(javacOutput.toPath(), - Charset.defaultCharset()); + String encoding = System.getProperty("native.encoding"); + Charset cs = (encoding != null) ? Charset.forName(encoding) : Charset.defaultCharset(); + List lines = Files.readAllLines(javacOutput.toPath(), cs); if (lines.size() != 1) { throw new AssertionError("The compiler output should have one line only"); } From 6b9ef8dadf17bfaac654f0ce8aa537e580109b18 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Thu, 28 Nov 2024 12:21:10 +0000 Subject: [PATCH 02/11] 8325906: Problemlist vmTestbase/vm/mlvm/meth/stress/compiler/deoptimize/Test.java#id1 until JDK-8320865 is fixed Backport-of: 2564f0f99866c33d14947609c276a421ce8cc0a2 --- test/hotspot/jtreg/ProblemList.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 5213d98286b..1ba00750ad6 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -174,6 +174,7 @@ vmTestbase/vm/mlvm/meth/stress/jdi/breakpointInCompiledCode/Test.java 8257761 ge vmTestbase/vm/mlvm/indy/func/jvmti/mergeCP_indy2none_a/TestDescription.java 8013267 generic-all vmTestbase/vm/mlvm/indy/func/jvmti/mergeCP_indy2manyDiff_b/TestDescription.java 8013267 generic-all vmTestbase/vm/mlvm/indy/func/jvmti/mergeCP_indy2manySame_b/TestDescription.java 8013267 generic-all +vmTestbase/vm/mlvm/meth/stress/compiler/deoptimize/Test.java#id1 8325905 generic-all vmTestbase/nsk/jdwp/ThreadReference/ForceEarlyReturn/forceEarlyReturn001/forceEarlyReturn001.java 7199837 generic-all From 5ac01dec3617efd6e51ea3256cba31c741336c13 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Thu, 28 Nov 2024 13:37:28 +0000 Subject: [PATCH 03/11] 8345055: [21u] ProblemList failing rtm tests on ppc platforms Reviewed-by: mdoerr --- test/hotspot/jtreg/ProblemList.txt | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 1ba00750ad6..350df071fdb 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -51,17 +51,18 @@ compiler/cpuflags/TestAESIntrinsicsOnSupportedConfig.java 8190680 generic-all compiler/runtime/Test8168712.java 8211769,8211771 generic-ppc64,generic-ppc64le,linux-s390x compiler/loopopts/TestUnreachableInnerLoop.java 8288981 linux-s390x -compiler/rtm/locking/TestRTMAbortRatio.java 8183263 generic-x64,generic-i586 -compiler/rtm/locking/TestRTMAbortThreshold.java 8183263,8313877 generic-x64,generic-i586,generic-ppc64le -compiler/rtm/locking/TestRTMAfterNonRTMDeopt.java 8183263 generic-x64,generic-i586 -compiler/rtm/locking/TestRTMDeoptOnHighAbortRatio.java 8183263 generic-x64,generic-i586 -compiler/rtm/locking/TestRTMDeoptOnLowAbortRatio.java 8183263,8307907 generic-x64,generic-i586,aix-ppc64 -compiler/rtm/locking/TestRTMLockingCalculationDelay.java 8183263 generic-x64,generic-i586 -compiler/rtm/locking/TestRTMLockingThreshold.java 8183263,8307907 generic-x64,generic-i586,aix-ppc64 -compiler/rtm/locking/TestRTMSpinLoopCount.java 8183263,8313877 generic-x64,generic-i586,generic-ppc64le -compiler/rtm/locking/TestUseRTMDeopt.java 8183263 generic-x64,generic-i586 -compiler/rtm/locking/TestUseRTMXendForLockBusy.java 8183263,8307907 generic-x64,generic-i586,aix-ppc64 -compiler/rtm/print/TestPrintPreciseRTMLockingStatistics.java 8183263,8307907 generic-x64,generic-i586,aix-ppc64 +compiler/rtm/locking/TestRTMAbortRatio.java 8183263 generic-x64,generic-i586,generic-ppc64le,generic-ppc64 +compiler/rtm/locking/TestRTMAbortThreshold.java 8183263,8313877 generic-x64,generic-i586,generic-ppc64le,generic-ppc64 +compiler/rtm/locking/TestRTMAfterNonRTMDeopt.java 8183263 generic-x64,generic-i586,generic-ppc64le,generic-ppc64 +compiler/rtm/locking/TestRTMDeoptOnHighAbortRatio.java 8183263 generic-x64,generic-i586,generic-ppc64le,generic-ppc64 +compiler/rtm/locking/TestRTMDeoptOnLowAbortRatio.java 8183263,8307907 generic-x64,generic-i586,generic-ppc64le,generic-ppc64 +compiler/rtm/locking/TestRTMLockingCalculationDelay.java 8183263 generic-x64,generic-i586,generic-ppc64le,generic-ppc64 +compiler/rtm/locking/TestRTMLockingThreshold.java 8183263,8307907 generic-x64,generic-i586,generic-ppc64le,generic-ppc64 +compiler/rtm/locking/TestRTMSpinLoopCount.java 8183263,8313877 generic-x64,generic-i586,generic-ppc64le,generic-ppc64 +compiler/rtm/locking/TestUseRTMDeopt.java 8183263 generic-x64,generic-i586,generic-ppc64le,generic-ppc64 +compiler/rtm/locking/TestUseRTMXendForLockBusy.java 8183263,8307907 generic-x64,generic-i586,generic-ppc64le,generic-ppc64 +compiler/rtm/print/TestPrintPreciseRTMLockingStatistics.java 8183263,8307907 generic-x64,generic-i586,generic-ppc64le,generic-ppc64 +compiler/rtm/cli/TestUseRTMDeoptOptionOnSupportedConfig.java 8183263,8307907 generic-ppc64le,generic-ppc64 compiler/c2/Test8004741.java 8235801 generic-all compiler/c2/irTests/TestDuplicateBackedge.java 8318904 generic-all From a803b5a05eedf93fc9c7a7f64f6aa78adf2fdcca Mon Sep 17 00:00:00 2001 From: Amit Kumar Date: Thu, 28 Nov 2024 21:04:45 +0000 Subject: [PATCH 04/11] 8342409: [s390x] C1 unwind_handler fails to unlock synchronized methods with LM_MONITOR Backport-of: 9201e9fcc28cff37cf9996e8db38f9aee7511b1c --- src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp | 6 +++++- src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp index 2b8edf0d9eb..52e8b0b541b 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp @@ -233,7 +233,11 @@ int LIR_Assembler::emit_unwind_handler() { LIR_Opr lock = FrameMap::as_opr(Z_R1_scratch); monitor_address(0, lock); stub = new MonitorExitStub(lock, true, 0); - __ unlock_object(Rtmp1, Rtmp2, lock->as_register(), *stub->entry()); + if (LockingMode == LM_MONITOR) { + __ branch_optimized(Assembler::bcondAlways, *stub->entry()); + } else { + __ unlock_object(Rtmp1, Rtmp2, lock->as_register(), *stub->entry()); + } __ bind(*stub->continuation()); } diff --git a/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp index 40edca6559a..63201a56499 100644 --- a/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp @@ -144,6 +144,8 @@ void C1_MacroAssembler::lock_object(Register Rmark, Register Roop, Register Rbox branch_optimized(Assembler::bcondNotZero, slow_case); // done bind(done); + } else { + assert(false, "Unhandled LockingMode:%d", LockingMode); } } @@ -180,6 +182,8 @@ void C1_MacroAssembler::unlock_object(Register Rmark, Register Roop, Register Rb // If the object header was not pointing to the displaced header, // we do unlocking via runtime call. branch_optimized(Assembler::bcondNotEqual, slow_case); + } else { + assert(false, "Unhandled LockingMode:%d", LockingMode); } // done bind(done); From 046c4aa16167134d1614be81a375d2d86e44077a Mon Sep 17 00:00:00 2001 From: Saint Wesonga Date: Thu, 28 Nov 2024 21:05:44 +0000 Subject: [PATCH 05/11] 8334475: UnsafeIntrinsicsTest.java#ZGenerationalDebug assert(!assert_on_failure) failed: Has low-order bits set Backport-of: 7f189a59d9ad803aee72565bc46a817d3f46f2e5 --- .../windows_aarch64/copy_windows_aarch64.hpp | 63 +++++++------------ 1 file changed, 23 insertions(+), 40 deletions(-) diff --git a/src/hotspot/os_cpu/windows_aarch64/copy_windows_aarch64.hpp b/src/hotspot/os_cpu/windows_aarch64/copy_windows_aarch64.hpp index ce2ad2d046f..96a34fe967b 100644 --- a/src/hotspot/os_cpu/windows_aarch64/copy_windows_aarch64.hpp +++ b/src/hotspot/os_cpu/windows_aarch64/copy_windows_aarch64.hpp @@ -26,8 +26,27 @@ #ifndef OS_CPU_WINDOWS_AARCH64_COPY_WINDOWS_AARCH64_HPP #define OS_CPU_WINDOWS_AARCH64_COPY_WINDOWS_AARCH64_HPP +#include "runtime/atomic.hpp" + #include +template +static void pd_conjoint_atomic_helper(const T* from, T* to, size_t count) { + if (from > to) { + while (count-- > 0) { + // Copy forwards + Atomic::store(to++, Atomic::load(from++)); + } + } else { + from += count - 1; + to += count - 1; + while (count-- > 0) { + // Copy backwards + Atomic::store(to--, Atomic::load(from--)); + } + } +} + static void pd_conjoint_words(const HeapWord* from, HeapWord* to, size_t count) { (void)memmove(to, from, count * HeapWordSize); } @@ -71,55 +90,19 @@ static void pd_conjoint_bytes_atomic(const void* from, void* to, size_t count) { } static void pd_conjoint_jshorts_atomic(const jshort* from, jshort* to, size_t count) { - if (from > to) { - while (count-- > 0) { - // Copy forwards - *to++ = *from++; - } - } else { - from += count - 1; - to += count - 1; - while (count-- > 0) { - // Copy backwards - *to-- = *from--; - } - } + pd_conjoint_atomic_helper(from, to, count); } static void pd_conjoint_jints_atomic(const jint* from, jint* to, size_t count) { - if (from > to) { - while (count-- > 0) { - // Copy forwards - *to++ = *from++; - } - } else { - from += count - 1; - to += count - 1; - while (count-- > 0) { - // Copy backwards - *to-- = *from--; - } - } + pd_conjoint_atomic_helper(from, to, count); } static void pd_conjoint_jlongs_atomic(const jlong* from, jlong* to, size_t count) { - pd_conjoint_oops_atomic((const oop*)from, (oop*)to, count); + pd_conjoint_atomic_helper(from, to, count); } static void pd_conjoint_oops_atomic(const oop* from, oop* to, size_t count) { - if (from > to) { - while (count-- > 0) { - // Copy forwards - *to++ = *from++; - } - } else { - from += count - 1; - to += count - 1; - while (count-- > 0) { - // Copy backwards - *to-- = *from--; - } - } + pd_conjoint_atomic_helper(from, to, count); } static void pd_arrayof_conjoint_bytes(const HeapWord* from, HeapWord* to, size_t count) { From 92aa77c5e8179633f8e9062ea2795842e8bdb365 Mon Sep 17 00:00:00 2001 From: Dmitry Cherepanov Date: Mon, 2 Dec 2024 09:49:25 +0000 Subject: [PATCH 06/11] 8343285: java.lang.Process is unresponsive and CPU usage spikes to 100% Backport-of: c00e20c399cf9b3b21258bd5654a92d703c8fcd2 --- src/hotspot/os/bsd/os_bsd.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 2aa5815a9b2..e04ae4924e2 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -1987,13 +1987,12 @@ jint os::init_2(void) { // On macOS according to setrlimit(2), OPEN_MAX must be used instead // of RLIM_INFINITY, but testing on macOS >= 10.6, reveals that - // we can, in fact, use even RLIM_INFINITY, so try the max value - // that the system claims can be used first, same as other BSD OSes. - // However, some terminals (ksh) will internally use "int" type - // to store this value and since RLIM_INFINITY overflows an "int" - // we might end up with a negative value, so cap the system limit max - // at INT_MAX instead, just in case, for everyone. - nbr_files.rlim_cur = MIN(INT_MAX, nbr_files.rlim_max); + // we can, in fact, use even RLIM_INFINITY. + // However, we need to limit the value to 0x100000 (which is the max value + // allowed on Linux) so that any existing code that iterates over all allowed + // file descriptors, finishes in a reasonable time, without appearing + // to hang. + nbr_files.rlim_cur = MIN(0x100000, nbr_files.rlim_max); status = setrlimit(RLIMIT_NOFILE, &nbr_files); if (status != 0) { From e5699972a4f202605a09e5d9612caf531bc6c292 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Mon, 2 Dec 2024 15:11:38 +0000 Subject: [PATCH 07/11] 8344628: Test TestEnableJVMCIProduct.java run with virtual thread intermittent fails Backport-of: 3a625f38aa4ab611fe5c7dffe420abce826d0d7e --- .../jvmci/TestEnableJVMCIProduct.java | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/test/hotspot/jtreg/compiler/jvmci/TestEnableJVMCIProduct.java b/test/hotspot/jtreg/compiler/jvmci/TestEnableJVMCIProduct.java index 1c36ab78ac6..16f9345c2ee 100644 --- a/test/hotspot/jtreg/compiler/jvmci/TestEnableJVMCIProduct.java +++ b/test/hotspot/jtreg/compiler/jvmci/TestEnableJVMCIProduct.java @@ -34,6 +34,11 @@ import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Collectors; + public class TestEnableJVMCIProduct { static class Expectation { @@ -52,10 +57,11 @@ static class Expectation { public static void main(String[] args) throws Exception { if (args.length != 0) { // Called as subprocess. Print system properties named by - // `args` and then exit. - for (String arg : args) { - System.out.printf("%s=%s%n", arg, System.getProperty(arg)); - } + // `args[1..]` to the file `args[0]` and then exit. + Files.writeString(Path.of(args[0]), + List.of(args).subList(1, args.length).stream() + .map(a -> "%s=%s".formatted(a, System.getProperty(a))) + .collect(Collectors.joining(","))); return; } // Test EnableJVMCIProduct without any other explicit JVMCI option @@ -80,16 +86,19 @@ public static void main(String[] args) throws Exception { new Expectation("UseJVMCICompiler", "true", "default")); } + static int id; + static void test(String explicitFlag, Expectation... expectations) throws Exception { String[] flags = {"-XX:+EnableJVMCIProduct", "-XX:+UseGraalJIT"}; String cwd = System.getProperty("user.dir"); for (String flag : flags) { + Path propsPath = Path.of("props." + id++); ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( "-XX:+UnlockExperimentalVMOptions", flag, "-XX:-UnlockExperimentalVMOptions", explicitFlag, "-XX:+PrintFlagsFinal", "--class-path=" + System.getProperty("java.class.path"), - "TestEnableJVMCIProduct", "jvmci.Compiler"); + "TestEnableJVMCIProduct", propsPath.toString(), "jvmci.Compiler"); OutputAnalyzer output = new OutputAnalyzer(pb.start()); for (Expectation expectation : expectations) { output.stdoutShouldMatch(expectation.pattern); @@ -103,7 +112,11 @@ static void test(String explicitFlag, Expectation... expectations) throws Except output.stdoutShouldMatch("No JVMCI compiler found"); } } else if (flag.equals("-XX:+UseGraalJIT")) { - output.shouldContain("jvmci.Compiler=graal"); + String props = Files.readString(propsPath); + String expect = "jvmci.Compiler=graal"; + if (!props.contains(expect)) { + throw new RuntimeException("\"%s\" does not contain \"%s\"".formatted(props, expect)); + } } } } From d383ba546ca749d21ad39288e47335135e7b2142 Mon Sep 17 00:00:00 2001 From: Goetz Lindenmaier Date: Mon, 2 Dec 2024 15:52:28 +0000 Subject: [PATCH 08/11] 8322809: SystemModulesMap::classNames and moduleNames arrays do not match the order Reviewed-by: sgehwolf, mbaesken Backport-of: f3be138eb80c9e7f6cc21afb75cda9e49b667c8a --- .../internal/plugins/SystemModulesPlugin.java | 13 +- .../ModuleMainClassTest.java | 145 ++++++++++++++++++ .../src/com.foo/com/foo/Main.java | 53 +++++++ .../src/com.foo/module-info.java | 27 ++++ .../src/net.foo/module-info.java | 25 +++ .../src/net.foo/net/foo/Main.java | 54 +++++++ 6 files changed, 312 insertions(+), 5 deletions(-) create mode 100644 test/jdk/tools/jlink/plugins/SystemModuleDescriptors/ModuleMainClassTest.java create mode 100644 test/jdk/tools/jlink/plugins/SystemModuleDescriptors/src/com.foo/com/foo/Main.java create mode 100644 test/jdk/tools/jlink/plugins/SystemModuleDescriptors/src/com.foo/module-info.java create mode 100644 test/jdk/tools/jlink/plugins/SystemModuleDescriptors/src/net.foo/module-info.java create mode 100644 test/jdk/tools/jlink/plugins/SystemModuleDescriptors/src/net.foo/net/foo/Main.java diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java index 320188f4c9b..4f9af79e931 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -1811,6 +1811,9 @@ private String genSystemModulesMapClass(ClassDesc allSystemModules, // write the class file to the pool as a resource String rn = "/java.base/" + SYSTEM_MODULES_MAP_CLASSNAME + ".class"; + // sort the map of module name to the class name of the generated SystemModules class + List> systemModulesMap = map.entrySet() + .stream().sorted(Map.Entry.comparingByKey()).toList(); ResourcePoolEntry e = ResourcePoolEntry.create(rn, Classfile.build( CD_SYSTEM_MODULES_MAP, clb -> clb.withFlags(ACC_FINAL + ACC_SUPER) @@ -1861,10 +1864,10 @@ private String genSystemModulesMapClass(ClassDesc allSystemModules, cob.anewarray(CD_String); int index = 0; - for (String moduleName : sorted(map.keySet())) { + for (Map.Entry entry : systemModulesMap) { cob.dup() // arrayref .constantInstruction(index) - .constantInstruction(moduleName) + .constantInstruction(entry.getKey()) .aastore(); index++; } @@ -1882,10 +1885,10 @@ private String genSystemModulesMapClass(ClassDesc allSystemModules, .anewarray(CD_String); int index = 0; - for (String className : sorted(map.values())) { + for (Map.Entry entry : systemModulesMap) { cob.dup() // arrayref .constantInstruction(index) - .constantInstruction(className.replace('/', '.')) + .constantInstruction(entry.getValue().replace('/', '.')) .aastore(); index++; } diff --git a/test/jdk/tools/jlink/plugins/SystemModuleDescriptors/ModuleMainClassTest.java b/test/jdk/tools/jlink/plugins/SystemModuleDescriptors/ModuleMainClassTest.java new file mode 100644 index 00000000000..ca7e2bc5f30 --- /dev/null +++ b/test/jdk/tools/jlink/plugins/SystemModuleDescriptors/ModuleMainClassTest.java @@ -0,0 +1,145 @@ +/* + * 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. + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.spi.ToolProvider; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import jdk.test.lib.compiler.CompilerUtils; +import jdk.test.lib.util.FileUtils; + +import static jdk.test.lib.process.ProcessTools.*; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; +import static org.junit.jupiter.api.Assertions.*; + +/** + * @test + * @bug 8322809 + * @library /test/lib + * @modules jdk.compiler jdk.jlink + * @build jdk.test.lib.compiler.CompilerUtils + * jdk.test.lib.process.ProcessTools + * jdk.test.lib.util.FileUtils + * ModuleMainClassTest + * @run junit ModuleMainClassTest + */ + +public class ModuleMainClassTest { + private static final String JAVA_HOME = System.getProperty("java.home"); + private static final String TEST_SRC = System.getProperty("test.src"); + + private static final Path SRC_DIR = Path.of(TEST_SRC, "src"); + private static final Path MODS_DIR = Path.of("mods"); + private static final Path JMODS_DIR = Path.of("jmods"); + + private static final Path IMAGE = Path.of("image"); + + // the module names are sorted by the plugin and so these names cover + // the cases that are before and after the elements of `jdk.*` modules + // with main classes + private static String[] modules = new String[] {"com.foo", "net.foo"}; + + private static boolean hasJmods() { + if (!Files.exists(Paths.get(JAVA_HOME, "jmods"))) { + System.err.println("Test skipped. NO jmods directory"); + return false; + } + return true; + } + + @BeforeAll + public static void compileAll() throws Throwable { + if (!hasJmods()) return; + + for (String mn : modules) { + Path msrc = SRC_DIR.resolve(mn); + assertTrue(CompilerUtils.compile(msrc, MODS_DIR, + "--module-source-path", SRC_DIR.toString(), + "--add-exports", "java.base/jdk.internal.module=" + mn)); + } + + if (Files.exists(IMAGE)) { + FileUtils.deleteFileTreeUnchecked(IMAGE); + } + + // create JMOD files + Files.createDirectories(JMODS_DIR); + Stream.of(modules).forEach(mn -> + assertTrue(jmod("create", + "--class-path", MODS_DIR.resolve(mn).toString(), + "--main-class", mn + ".Main", + JMODS_DIR.resolve(mn + ".jmod").toString()) == 0) + ); + + // the run-time image created will have 4 modules with main classes + createImage(IMAGE, "com.foo"); + } + + @Test + public void testComFoo() throws Exception { + if (!hasJmods()) return; + + Path java = IMAGE.resolve("bin").resolve("java"); + assertTrue(executeProcess(java.toString(), + "-m", "com.foo") + .outputTo(System.out) + .errorTo(System.out) + .getExitValue() == 0); + } + + @Test + public void testNetFoo() throws Exception { + if (!hasJmods()) return; + + Path java = IMAGE.resolve("bin").resolve("java"); + assertTrue(executeProcess(java.toString(), + "-m", "net.foo") + .outputTo(System.out) + .errorTo(System.out) + .getExitValue() == 0); + } + + static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink") + .orElseThrow(() -> new RuntimeException("jlink tool not found")); + + static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod") + .orElseThrow(() -> new RuntimeException("jmod tool not found")); + + private static void createImage(Path outputDir, String... modules) throws Throwable { + assertTrue(JLINK_TOOL.run(System.out, System.out, + "--output", outputDir.toString(), + "--add-modules", Arrays.stream(modules).collect(Collectors.joining(",")), + "--module-path", JMODS_DIR.toString()) == 0); + } + + private static int jmod(String... options) { + System.out.println("jmod " + Arrays.asList(options)); + return JMOD_TOOL.run(System.out, System.out, options); + } +} diff --git a/test/jdk/tools/jlink/plugins/SystemModuleDescriptors/src/com.foo/com/foo/Main.java b/test/jdk/tools/jlink/plugins/SystemModuleDescriptors/src/com.foo/com/foo/Main.java new file mode 100644 index 00000000000..4cf87ea3ce7 --- /dev/null +++ b/test/jdk/tools/jlink/plugins/SystemModuleDescriptors/src/com.foo/com/foo/Main.java @@ -0,0 +1,53 @@ +/* + * 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 com.foo; + +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.util.stream.Stream; + +/** + * Sanity test if SystemModules pre-resolved at link-time for com.foo + * with main class is loaded properly. + */ +public class Main { + public static void main(String... args) throws Exception { + ModuleDescriptor md = Main.class.getModule().getDescriptor(); + System.out.println(md); + + checkMainClass("com.foo", "com.foo.Main"); + checkMainClass("net.foo", "net.foo.Main"); + Stream.of("jdk.httpserver").forEach(mn -> + ModuleFinder.ofSystem().find(mn).get().descriptor().mainClass() + .orElseThrow(() -> new RuntimeException(mn + " no main class")) + ); + } + + static void checkMainClass(String mn, String mainClass) { + String cn = ModuleFinder.ofSystem().find(mn).get().descriptor().mainClass().get(); + if (!cn.equals(mainClass)) { + throw new RuntimeException("Mismatched main class of module " + mn + ": " + cn + " expected: " + mainClass); + } + } +} diff --git a/test/jdk/tools/jlink/plugins/SystemModuleDescriptors/src/com.foo/module-info.java b/test/jdk/tools/jlink/plugins/SystemModuleDescriptors/src/com.foo/module-info.java new file mode 100644 index 00000000000..99107602506 --- /dev/null +++ b/test/jdk/tools/jlink/plugins/SystemModuleDescriptors/src/com.foo/module-info.java @@ -0,0 +1,27 @@ +/* + * 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. + */ + +module com.foo { + requires jdk.httpserver; + requires net.foo; +} diff --git a/test/jdk/tools/jlink/plugins/SystemModuleDescriptors/src/net.foo/module-info.java b/test/jdk/tools/jlink/plugins/SystemModuleDescriptors/src/net.foo/module-info.java new file mode 100644 index 00000000000..b8bd32868f9 --- /dev/null +++ b/test/jdk/tools/jlink/plugins/SystemModuleDescriptors/src/net.foo/module-info.java @@ -0,0 +1,25 @@ +/* + * 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. + */ + +module net.foo { +} diff --git a/test/jdk/tools/jlink/plugins/SystemModuleDescriptors/src/net.foo/net/foo/Main.java b/test/jdk/tools/jlink/plugins/SystemModuleDescriptors/src/net.foo/net/foo/Main.java new file mode 100644 index 00000000000..3c72a6a63d5 --- /dev/null +++ b/test/jdk/tools/jlink/plugins/SystemModuleDescriptors/src/net.foo/net/foo/Main.java @@ -0,0 +1,54 @@ +/* + * 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 net.foo; + +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.util.stream.Stream; + +/** + * Sanity test if SystemModules pre-resolved at link-time for net.foo + * with main class is loaded properly. + */ +public class Main { + public static void main(String... args) throws Exception { + ModuleDescriptor md = Main.class.getModule().getDescriptor(); + System.out.println(md); + + checkMainClass("com.foo", "com.foo.Main"); + checkMainClass("net.foo", "net.foo.Main"); + Stream.of("jdk.httpserver").forEach(mn -> + ModuleFinder.ofSystem().find(mn).get().descriptor().mainClass() + .orElseThrow(() -> new RuntimeException(mn + " no main class")) + ); + } + + static void checkMainClass(String mn, String mainClass) { + String cn = ModuleFinder.ofSystem().find(mn).get().descriptor().mainClass().get(); + if (!cn.equals(mainClass)) { + throw new RuntimeException("Mismatched main class of module " + mn + ": " + cn + " expected: " + mainClass); + } + } + +} From 96a3a88ee206680ba06c6ed876b538de41ff511c Mon Sep 17 00:00:00 2001 From: Martin Balao Date: Tue, 3 Dec 2024 15:25:26 +0000 Subject: [PATCH 09/11] 8344993: [21u] [REDO] Backport JDK-8327501 and JDK-8328366 to JDK 21 Reviewed-by: sgehwolf --- .../share/classes/java/util/concurrent/ForkJoinPool.java | 4 +--- test/jdk/java/util/concurrent/tck/ForkJoinPool9Test.java | 8 ++++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java b/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java index 5e698b1540f..8aafda5312e 100644 --- a/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java +++ b/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java @@ -981,9 +981,7 @@ public final ForkJoinWorkerThread newThread(ForkJoinPool pool) { boolean isCommon = (pool.workerNamePrefix == null); @SuppressWarnings("removal") SecurityManager sm = System.getSecurityManager(); - if (sm == null) - return new ForkJoinWorkerThread(null, pool, true, false); - else if (isCommon) + if (sm != null && isCommon) return newCommonWithACC(pool); else return newRegularWithACC(pool); diff --git a/test/jdk/java/util/concurrent/tck/ForkJoinPool9Test.java b/test/jdk/java/util/concurrent/tck/ForkJoinPool9Test.java index 266c2a036fe..a87aa7b916b 100644 --- a/test/jdk/java/util/concurrent/tck/ForkJoinPool9Test.java +++ b/test/jdk/java/util/concurrent/tck/ForkJoinPool9Test.java @@ -79,6 +79,9 @@ public void testCommonPoolThreadContextClassLoader() throws Throwable { assertSame(ForkJoinPool.commonPool(), ForkJoinTask.getPool()); Thread currentThread = Thread.currentThread(); + ClassLoader preexistingContextClassLoader = + currentThread.getContextClassLoader(); + Stream.of(systemClassLoader, null).forEach(cl -> { if (randomBoolean()) // should always be permitted, without effect @@ -95,6 +98,11 @@ public void testCommonPoolThreadContextClassLoader() throws Throwable { () -> System.getProperty("foo"), () -> currentThread.setContextClassLoader( classLoaderDistinctFromSystemClassLoader)); + else { + currentThread.setContextClassLoader(classLoaderDistinctFromSystemClassLoader); + assertSame(currentThread.getContextClassLoader(), classLoaderDistinctFromSystemClassLoader); + currentThread.setContextClassLoader(preexistingContextClassLoader); + } // TODO ? // if (haveSecurityManager // && Thread.currentThread().getClass().getSimpleName() From 3f648b4f65dac89d91ae318f410e66e596589f3b Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Tue, 3 Dec 2024 19:05:42 +0000 Subject: [PATCH 10/11] 8342905: Thread.setContextClassloader from thread in FJP commonPool task no longer works after JDK-8327501 redux Reviewed-by: sgehwolf, mbalao Backport-of: 872ae1347198408ffd62559ca8c1a7420c4a8108 --- src/java.base/share/classes/java/lang/System.java | 5 +++++ .../share/classes/java/util/concurrent/ForkJoinPool.java | 8 +++++--- .../share/classes/jdk/internal/access/JavaLangAccess.java | 6 ++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 53c4ed4ddda..e748e78c6a7 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -2672,6 +2672,11 @@ public StackWalker newStackWalkerInstance(Set options, public String getLoaderNameID(ClassLoader loader) { return loader != null ? loader.nameAndId() : "null"; } + + @Override + public boolean allowSecurityManager() { + return System.allowSecurityManager(); + } }); } } diff --git a/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java b/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java index 8aafda5312e..808dcdac974 100644 --- a/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java +++ b/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java @@ -52,6 +52,8 @@ import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.Condition; + +import jdk.internal.access.JavaLangAccess; import jdk.internal.access.JavaUtilConcurrentFJPAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.misc.Unsafe; @@ -979,9 +981,7 @@ static final class DefaultForkJoinWorkerThreadFactory implements ForkJoinWorkerThreadFactory { public final ForkJoinWorkerThread newThread(ForkJoinPool pool) { boolean isCommon = (pool.workerNamePrefix == null); - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null && isCommon) + if (isCommon && JLA.allowSecurityManager()) return newCommonWithACC(pool); else return newRegularWithACC(pool); @@ -998,6 +998,8 @@ public final ForkJoinWorkerThread newThread(ForkJoinPool pool) { @SuppressWarnings("removal") static volatile AccessControlContext regularACC, commonACC; + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + @SuppressWarnings("removal") static ForkJoinWorkerThread newRegularWithACC(ForkJoinPool pool) { AccessControlContext acc = regularACC; diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index babc1940e78..22ad4444ee1 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -579,4 +579,10 @@ StackWalker newStackWalkerInstance(Set options, * explicitly set otherwise @ */ String getLoaderNameID(ClassLoader loader); + + /** + * Is a security manager already set or allowed to be set + * (using -Djava.security.manager=allow)? + */ + boolean allowSecurityManager(); } From e45287d1ad9dcadf8a23d3271f1b675b8dade0ac Mon Sep 17 00:00:00 2001 From: Alexey Bakhtin Date: Wed, 4 Dec 2024 01:02:53 +0000 Subject: [PATCH 11/11] 8335912: Add an operation mode to the jar command when extracting to not overwriting existing files Reviewed-by: henryjen, goetz, mbalao Backport-of: 158b93d19a518d2b9d3d185e2d4c4dbff9c82aab --- .../sun/tools/jar/GNUStyleOptions.java | 8 + .../share/classes/sun/tools/jar/Main.java | 15 +- .../sun/tools/jar/resources/jar.properties | 18 +- test/jdk/tools/jar/ExtractFilesTest.java | 265 ++++++++++++++++++ test/jdk/tools/jar/MultipleManifestTest.java | 224 +++++++++++++++ 5 files changed, 528 insertions(+), 2 deletions(-) create mode 100644 test/jdk/tools/jar/ExtractFilesTest.java create mode 100644 test/jdk/tools/jar/MultipleManifestTest.java diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java b/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java index b526f80cb35..3cb971efb42 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java +++ b/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java @@ -212,6 +212,13 @@ void process(Main jartool, String opt, String arg) throws BadArgs { } }, + // Extract options + new Option(false, OptionType.EXTRACT, "--keep-old-files", "-k") { + void process(Main jartool, String opt, String arg) { + jartool.kflag = true; + } + }, + // Hidden options new Option(false, OptionType.OTHER, "-P") { void process(Main jartool, String opt, String arg) { @@ -254,6 +261,7 @@ enum OptionType { CREATE("create"), CREATE_UPDATE("create.update"), CREATE_UPDATE_INDEX("create.update.index"), + EXTRACT("extract"), OTHER("other"); /** Resource lookup section prefix. */ diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java index 6163ef82647..852cb179284 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java +++ b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java @@ -155,8 +155,9 @@ public int hashCode() { * nflag: Perform jar normalization at the end * pflag: preserve/don't strip leading slash and .. component from file name * dflag: print module descriptor + * kflag: keep existing file */ - boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag, pflag, dflag, validate; + boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag, pflag, dflag, kflag, validate; boolean suppressDeprecateMsg = false; @@ -590,6 +591,9 @@ boolean parseArgs(String args[]) { case '0': flag0 = true; break; + case 'k': + kflag = true; + break; case 'i': if (cflag || uflag || xflag || tflag) { usageError(getMsg("error.multiple.main.operations")); @@ -620,6 +624,9 @@ boolean parseArgs(String args[]) { usageError(getMsg("error.bad.option")); return false; } + if (kflag && !xflag) { + warn(formatMsg("warn.option.is.ignored", "--keep-old-files/-k/k")); + } /* parse file arguments */ int n = args.length - count; @@ -1470,6 +1477,12 @@ ZipEntry extractFile(InputStream is, ZipEntry e) throws IOException { output(formatMsg("out.create", name)); } } else { + if (f.exists() && kflag) { + if (vflag) { + output(formatMsg("out.kept", name)); + } + return rc; + } if (f.getParent() != null) { File d = new File(f.getParent()); if (!d.exists() && !d.mkdirs() || !d.isDirectory()) { diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties index d0cb53b8a0d..10960a4c4ee 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties +++ b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties @@ -143,6 +143,8 @@ warn.index.is.ignored=\ The JAR index (META-INF/INDEX.LIST) is ignored at run-time since JDK 18 warn.flag.is.deprecated=\ Warning: The {0} option is deprecated, and may be ignored or removed in a future release\n +warn.option.is.ignored=\ + Warning: The {0} option is not valid with current usage, will be ignored. out.added.manifest=\ added manifest out.added.module-info=\ @@ -165,6 +167,8 @@ out.create=\ \ \ created: {0} out.extracted=\ extracted: {0} +out.kept=\ + \ \ skipped: {0} exists out.inflated=\ \ inflated: {0} out.size=\ @@ -245,7 +249,10 @@ main.help.opt.main.list=\ main.help.opt.main.update=\ \ -u, --update Update an existing jar archive main.help.opt.main.extract=\ -\ -x, --extract Extract named (or all) files from the archive +\ -x, --extract Extract named (or all) files from the archive.\n\ +\ If a file with the same name appears more than once in\n\ +\ the archive, each copy will be extracted, with later copies\n\ +\ overwriting (replacing) earlier copies unless -k is specified. main.help.opt.main.describe-module=\ \ -d, --describe-module Print the module descriptor, or automatic module name main.help.opt.main.validate=\ @@ -307,6 +314,15 @@ main.help.opt.create.update.index.date=\ \ --date=TIMESTAMP The timestamp in ISO-8601 extended offset date-time with\n\ \ optional time-zone format, to use for the timestamps of\n\ \ entries, e.g. "2022-02-12T12:30:00-05:00" +main.help.opt.extract=\ +\ Operation modifiers valid only in extract mode:\n +main.help.opt.extract.keep-old-files=\ +\ -k, --keep-old-files Do not overwrite existing files.\n\ +\ If a Jar file entry with the same name exists in the target\n\ +\ directory, the existing file will not be overwritten.\n\ +\ As a result, if a file appears more than once in an\n\ +\ archive, later copies will not overwrite earlier copies.\n\ +\ Also note that some file system can be case insensitive. main.help.opt.other=\ \ Other options:\n main.help.opt.other.help=\ diff --git a/test/jdk/tools/jar/ExtractFilesTest.java b/test/jdk/tools/jar/ExtractFilesTest.java new file mode 100644 index 00000000000..b829b770fc8 --- /dev/null +++ b/test/jdk/tools/jar/ExtractFilesTest.java @@ -0,0 +1,265 @@ +/* + * 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 + * @bug 8335912 + * @summary test extract jar files overwrite existing files behavior + * @library /test/lib + * @modules jdk.jartool + * @build jdk.test.lib.Platform + * jdk.test.lib.util.FileUtils + * @run junit/othervm ExtractFilesTest + */ + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.spi.ToolProvider; +import java.util.stream.Stream; + +import jdk.test.lib.util.FileUtils; + + @TestInstance(Lifecycle.PER_CLASS) + public class ExtractFilesTest { + private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar") + .orElseThrow(() -> + new RuntimeException("jar tool not found") + ); + + private final String nl = System.lineSeparator(); + private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + private final PrintStream out = new PrintStream(baos); + + @BeforeAll + public void setupJar() throws IOException { + mkdir("test1 test2"); + echo("testfile1", "test1/testfile1"); + echo("testfile2", "test2/testfile2"); + jar("cf test.jar -C test1 . -C test2 ."); + rm("test1 test2"); + } + + @AfterAll + public void cleanup() { + rm("test.jar"); + } + + /** + * Regular clean extract with expected output. + */ + @Test + public void testExtract() throws IOException { + jar("xvf test.jar"); + println(); + String output = " created: META-INF/" + nl + + " inflated: META-INF/MANIFEST.MF" + nl + + " inflated: testfile1" + nl + + " inflated: testfile2" + nl; + rm("META-INF testfile1 testfile2"); + Assertions.assertArrayEquals(baos.toByteArray(), output.getBytes()); + } + + /** + * Extract should overwrite existing file as default behavior. + */ + @Test + public void testOverwrite() throws IOException { + touch("testfile1"); + jar("xvf test.jar"); + println(); + String output = " created: META-INF/" + nl + + " inflated: META-INF/MANIFEST.MF" + nl + + " inflated: testfile1" + nl + + " inflated: testfile2" + nl; + Assertions.assertEquals("testfile1", cat("testfile1")); + rm("META-INF testfile1 testfile2"); + Assertions.assertArrayEquals(baos.toByteArray(), output.getBytes()); + } + + /** + * Extract with legacy style option `k` should preserve existing files. + */ + @Test + public void testKeptOldFile() throws IOException { + touch("testfile1"); + jar("xkvf test.jar"); + println(); + String output = " created: META-INF/" + nl + + " inflated: META-INF/MANIFEST.MF" + nl + + " skipped: testfile1 exists" + nl + + " inflated: testfile2" + nl; + Assertions.assertEquals("", cat("testfile1")); + Assertions.assertEquals("testfile2", cat("testfile2")); + rm("META-INF testfile1 testfile2"); + Assertions.assertArrayEquals(baos.toByteArray(), output.getBytes()); + } + + /** + * Extract with gnu style -k should preserve existing files. + */ + @Test + public void testGnuOptionsKeptOldFile() throws IOException { + touch("testfile1 testfile2"); + jar("-x -k -v -f test.jar"); + println(); + String output = " created: META-INF/" + nl + + " inflated: META-INF/MANIFEST.MF" + nl + + " skipped: testfile1 exists" + nl + + " skipped: testfile2 exists" + nl; + Assertions.assertEquals("", cat("testfile1")); + Assertions.assertEquals("", cat("testfile2")); + rm("META-INF testfile1 testfile2"); + Assertions.assertArrayEquals(baos.toByteArray(), output.getBytes()); + } + + /** + * Extract with gnu style long option --keep-old-files should preserve existing files. + */ + @Test + public void testGnuLongOptionsKeptOldFile() throws IOException { + touch("testfile2"); + jar("-x --keep-old-files -v -f test.jar"); + println(); + String output = " created: META-INF/" + nl + + " inflated: META-INF/MANIFEST.MF" + nl + + " inflated: testfile1" + nl + + " skipped: testfile2 exists" + nl; + Assertions.assertEquals("testfile1", cat("testfile1")); + Assertions.assertEquals("", cat("testfile2")); + rm("META-INF testfile1 testfile2"); + Assertions.assertArrayEquals(baos.toByteArray(), output.getBytes()); + } + + /** + * Test jar will issue warning when use keep option in non-extraction mode. + */ + @Test + public void testWarningOnInvalidKeepOption() throws IOException { + var err = jar("tkf test.jar"); + println(); + + String output = "META-INF/" + nl + + "META-INF/MANIFEST.MF" + nl + + "testfile1" + nl + + "testfile2" + nl; + + Assertions.assertArrayEquals(baos.toByteArray(), output.getBytes()); + Assertions.assertEquals("Warning: The --keep-old-files/-k/k option is not valid with current usage, will be ignored." + nl, err); + } + + private Stream mkpath(String... args) { + return Arrays.stream(args).map(d -> Path.of(".", d.split("/"))); + } + + private void mkdir(String cmdline) { + System.out.println("mkdir -p " + cmdline); + mkpath(cmdline.split(" +")).forEach(p -> { + try { + Files.createDirectories(p); + } catch (IOException x) { + throw new UncheckedIOException(x); + } + }); + } + + private void touch(String cmdline) { + System.out.println("touch " + cmdline); + mkpath(cmdline.split(" +")).forEach(p -> { + try { + Files.createFile(p); + } catch (IOException x) { + throw new UncheckedIOException(x); + } + }); + } + + private void echo(String text, String path) { + System.out.println("echo '" + text + "' > " + path); + try { + var p = Path.of(".", path.split("/")); + Files.writeString(p, text); + } catch (IOException x) { + throw new UncheckedIOException(x); + } + } + + private String cat(String path) { + System.out.println("cat " + path); + try { + return Files.readString(Path.of(path)); + } catch (IOException x) { + throw new UncheckedIOException(x); + } + } + + private void rm(String cmdline) { + System.out.println("rm -rf " + cmdline); + mkpath(cmdline.split(" +")).forEach(p -> { + try { + if (Files.isDirectory(p)) { + FileUtils.deleteFileTreeWithRetry(p); + } else { + FileUtils.deleteFileIfExistsWithRetry(p); + } + } catch (IOException x) { + throw new UncheckedIOException(x); + } + }); + } + + private String jar(String cmdline) throws IOException { + System.out.println("jar " + cmdline); + baos.reset(); + + // the run method catches IOExceptions, we need to expose them + ByteArrayOutputStream baes = new ByteArrayOutputStream(); + PrintStream err = new PrintStream(baes); + PrintStream saveErr = System.err; + System.setErr(err); + try { + int rc = JAR_TOOL.run(out, err, cmdline.split(" +")); + if (rc != 0) { + throw new IOException(baes.toString()); + } + } finally { + System.setErr(saveErr); + } + return baes.toString(); + } + + private void println() throws IOException { + System.out.println(new String(baos.toByteArray())); + } +} diff --git a/test/jdk/tools/jar/MultipleManifestTest.java b/test/jdk/tools/jar/MultipleManifestTest.java new file mode 100644 index 00000000000..951ce4bb890 --- /dev/null +++ b/test/jdk/tools/jar/MultipleManifestTest.java @@ -0,0 +1,224 @@ +/* + * 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 + * @bug 8335912 + * @summary test extract jar with multpile manifest files + * @library /test/lib + * @modules jdk.jartool + * @build jdk.test.lib.Platform + * jdk.test.lib.util.FileUtils + * @run junit/othervm MultipleManifestTest + */ + +import java.io.ByteArrayOutputStream; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; + +import java.io.IOException; +import java.io.PrintStream; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.spi.ToolProvider; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import jdk.test.lib.util.FileUtils; + +@TestInstance(Lifecycle.PER_CLASS) +class MultipleManifestTest { + private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar") + .orElseThrow(() -> + new RuntimeException("jar tool not found") + ); + + private final String nl = System.lineSeparator(); + private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + private final PrintStream jarOut = new PrintStream(baos); + + static final Path zip = Path.of("MultipleManifestTest.jar"); + static final String jdkVendor = System.getProperty("java.vendor"); + static final String jdkVersion = System.getProperty("java.version"); + static final String MANIFEST1 = "Manifest-Version: 1.0" + + System.lineSeparator() + + "Created-By: " + jdkVersion + " (" + jdkVendor + ")"; + static final String MANIFEST2 = "Manifest-Version: 2.0" + + System.lineSeparator() + + "Created-By: " + jdkVersion + " (" + jdkVendor + ")"; + static final String MANIFEST3 = "Manifest-Version: 3.0" + + System.lineSeparator() + + "Created-By: " + jdkVersion + " (" + jdkVendor + ")"; + private static final String META_INF = "META-INF/"; + + /** + * Delete the ZIP file produced by this test + * + * @throws IOException if an unexpected IOException occurs + */ + @AfterAll + public void cleanup() throws IOException { + Files.deleteIfExists(zip); + } + + /** + * Create a JAR with the Manifest as the 1st, 2nd and 4th entry + * + * @throws IOException if an error occurs + */ + @BeforeAll + public void writeManifestAsFirstSecondAndFourthEntry() throws IOException { + int locPosA, locPosB, cenPos; + System.out.printf("%n%n*****Creating Jar with the Manifest as the 1st, 2nd and 4th entry*****%n%n"); + var out = new ByteArrayOutputStream(1024); + try (var zos = new ZipOutputStream(out)) { + zos.putNextEntry(new ZipEntry(JarFile.MANIFEST_NAME)); + zos.write(MANIFEST1.getBytes(StandardCharsets.UTF_8)); + zos.closeEntry(); + locPosA = out.size(); + zos.putNextEntry(new ZipEntry(META_INF + "AANIFEST.MF")); + zos.write(MANIFEST2.getBytes(StandardCharsets.UTF_8)); + zos.putNextEntry(new ZipEntry("entry1.txt")); + zos.write("entry1".getBytes(StandardCharsets.UTF_8)); + zos.closeEntry(); + locPosB = out.size(); + zos.putNextEntry(new ZipEntry(META_INF + "BANIFEST.MF")); + zos.write(MANIFEST3.getBytes(StandardCharsets.UTF_8)); + zos.putNextEntry(new ZipEntry("entry2.txt")); + zos.write("hello entry2".getBytes(StandardCharsets.UTF_8)); + zos.flush(); + cenPos = out.size(); + } + var template = out.toByteArray(); + // ISO_8859_1 to keep the 8-bit value + var s = new String(template, StandardCharsets.ISO_8859_1); + // change META-INF/AANIFEST.MF to META-INF/MANIFEST.MF + var loc = s.indexOf("AANIFEST.MF", locPosA); + var cen = s.indexOf("AANIFEST.MF", cenPos); + template[loc] = template[cen] = (byte) 'M'; + // change META-INF/BANIFEST.MF to META-INF/MANIFEST.MF + loc = s.indexOf("BANIFEST.MF", locPosB); + cen = s.indexOf("BANIFEST.MF", cenPos); + template[loc] = template[cen] = (byte) 'M'; + Files.write(zip, template); + } + + @AfterEach + public void removeExtractedFiles() { + rm("META-INF entry1.txt entry2.txt"); + } + + /** + * Extract by default should have the last manifest. + */ + @Test + public void testOverwrite() throws IOException { + jar("xvf " + zip.toString()); + println(); + Assertions.assertEquals("3.0", getManifestVersion()); + String output = " inflated: META-INF/MANIFEST.MF" + nl + + " inflated: META-INF/MANIFEST.MF" + nl + + " inflated: entry1.txt" + nl + + " inflated: META-INF/MANIFEST.MF" + nl + + " inflated: entry2.txt" + nl; + Assertions.assertArrayEquals(baos.toByteArray(), output.getBytes()); + } + + /** + * Extract with k option should have first manifest. + */ + @Test + public void testKeptOldFile() throws IOException { + jar("xkvf " + zip.toString()); + println(); + Assertions.assertEquals("1.0", getManifestVersion()); + String output = " inflated: META-INF/MANIFEST.MF" + nl + + " skipped: META-INF/MANIFEST.MF exists" + nl + + " inflated: entry1.txt" + nl + + " skipped: META-INF/MANIFEST.MF exists" + nl + + " inflated: entry2.txt" + nl; + Assertions.assertArrayEquals(baos.toByteArray(), output.getBytes()); + } + + private String getManifestVersion() throws IOException { + try (var is = Files.newInputStream(Path.of(JarFile.MANIFEST_NAME))) { + var manifest = new Manifest(is); + return manifest.getMainAttributes().getValue(Attributes.Name.MANIFEST_VERSION); + } + } + + private void jar(String cmdline) throws IOException { + System.out.println("jar " + cmdline); + baos.reset(); + + // the run method catches IOExceptions, we need to expose them + ByteArrayOutputStream baes = new ByteArrayOutputStream(); + PrintStream err = new PrintStream(baes); + PrintStream saveErr = System.err; + System.setErr(err); + try { + int rc = JAR_TOOL.run(jarOut, err, cmdline.split(" +")); + if (rc != 0) { + throw new IOException(baes.toString()); + } + } finally { + System.setErr(saveErr); + } + } + + private void println() throws IOException { + System.out.println(new String(baos.toByteArray())); + } + + private Stream mkpath(String... args) { + return Arrays.stream(args).map(d -> Path.of(".", d.split("/"))); + } + + private void rm(String cmdline) { + System.out.println("rm -rf " + cmdline); + mkpath(cmdline.split(" +")).forEach(p -> { + try { + if (Files.isDirectory(p)) { + FileUtils.deleteFileTreeWithRetry(p); + } else { + FileUtils.deleteFileIfExistsWithRetry(p); + } + } catch (IOException x) { + throw new UncheckedIOException(x); + } + }); + } +} \ No newline at end of file