diff --git a/make/CompileInterimLangtools.gmk b/make/CompileInterimLangtools.gmk index c7d1c3796f6..8254b8fc0a7 100644 --- a/make/CompileInterimLangtools.gmk +++ b/make/CompileInterimLangtools.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, 2026, 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 @@ -68,17 +68,19 @@ java.compiler.interim_EXTRA_FILES := \ TARGETS += $(BUILDTOOLS_OUTPUTDIR)/gensrc/java.compiler.interim/javax/tools/ToolProvider.java ################################################################################ -# Use the up-to-date PreviewFeature.java and NoPreview.java from the current -# sources, instead of the versions from the boot JDK, as javac may be referring -# to constants from the up-to-date versions. +# Create a hybrid PreviewFeature.java that combines constants +# from the current sources, as those can be used in javac APIs, and from the +# bootstrap JDK, as those can be used from bootstrap JDK classfiles. -$(eval $(call SetupCopyFiles, COPY_PREVIEW_FEATURES, \ - FILES := $(TOPDIR)/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java \ - $(TOPDIR)/src/java.base/share/classes/jdk/internal/javac/NoPreview.java, \ - DEST := $(BUILDTOOLS_OUTPUTDIR)/gensrc/java.base.interim/jdk/internal/javac/, \ -)) +$(BUILDTOOLS_OUTPUTDIR)/gensrc/java.base.interim/jdk/internal/javac/PreviewFeature.java: \ + $(TOPDIR)/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java + $(call LogInfo, Generating $@) + $(JAVA) $(TOPDIR)/make/langtools/tools/previewfeature/SetupPreviewFeature.java \ + $(TOPDIR)/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java \ + $@ -TARGETS += $(COPY_PREVIEW_FEATURES) + +TARGETS += $(BUILDTOOLS_OUTPUTDIR)/gensrc/java.base.interim/jdk/internal/javac/PreviewFeature.java ################################################################################ # Setup the rules to build interim langtools, which is compiled by the boot @@ -123,7 +125,8 @@ define SetupInterimModule $1_DEPS_INTERIM := $$(addsuffix .interim, $$(filter \ $$(INTERIM_LANGTOOLS_BASE_MODULES), $$(call FindTransitiveDepsForModule, $1))) - $$(BUILD_$1.interim): $$(foreach d, $$($1_DEPS_INTERIM), $$(BUILD_$$d)) $(COPY_PREVIEW_FEATURES) + $$(BUILD_$1.interim): $$(foreach d, $$($1_DEPS_INTERIM), $$(BUILD_$$d)) \ + $(BUILDTOOLS_OUTPUTDIR)/gensrc/java.base.interim/jdk/internal/javac/PreviewFeature.java TARGETS += $$(BUILD_$1.interim) endef diff --git a/make/GenerateLinkOptData.gmk b/make/GenerateLinkOptData.gmk index 6f6e1f29b4c..d615a34e71a 100644 --- a/make/GenerateLinkOptData.gmk +++ b/make/GenerateLinkOptData.gmk @@ -70,12 +70,15 @@ CLASSLIST_FILE_VM_OPTS = \ # Save the stderr output of the command and print it along with stdout in case # something goes wrong. +# The classlists must be generated with -Xint to avoid non-determinism +# introduced by JIT compiled code $(CLASSLIST_FILE): $(INTERIM_IMAGE_DIR)/bin/java$(EXECUTABLE_SUFFIX) $(CLASSLIST_JAR) $(call MakeDir, $(LINK_OPT_DIR)) $(call LogInfo, Generating $(patsubst $(OUTPUTDIR)/%, %, $@)) $(call LogInfo, Generating $(patsubst $(OUTPUTDIR)/%, %, $(JLI_TRACE_FILE))) $(FIXPATH) $(INTERIM_IMAGE_DIR)/bin/java -XX:DumpLoadedClassList=$@.raw \ $(CLASSLIST_FILE_VM_OPTS) \ + -Xint \ -Xlog:aot=off \ -Xlog:cds=off \ -cp $(SUPPORT_OUTPUTDIR)/classlist.jar \ @@ -90,6 +93,7 @@ $(CLASSLIST_FILE): $(INTERIM_IMAGE_DIR)/bin/java$(EXECUTABLE_SUFFIX) $(CLASSLIST -XX:SharedClassListFile=$@.interim -XX:SharedArchiveFile=$@.jsa \ -Djava.lang.invoke.MethodHandle.TRACE_RESOLVE=true \ $(CLASSLIST_FILE_VM_OPTS) \ + -Xint \ --module-path $(SUPPORT_OUTPUTDIR)/classlist.jar \ -Xlog:aot=off \ -Xlog:cds=off \ diff --git a/make/autoconf/basic_tools.m4 b/make/autoconf/basic_tools.m4 index 8e42f9205a4..66ef94d48a8 100644 --- a/make/autoconf/basic_tools.m4 +++ b/make/autoconf/basic_tools.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2026, 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 @@ -369,6 +369,10 @@ AC_DEFUN_ONCE([BASIC_SETUP_COMPLEX_TOOLS], IS_GNU_DATE=yes else AC_MSG_RESULT([no]) + # Likely at the AIX provided version of the date utility here, which is not compatible + if test "x$OPENJDK_TARGET_OS" = "xaix"; then + AC_MSG_ERROR([gnu date from AIX toolbox is required]) + fi IS_GNU_DATE=no fi AC_SUBST(IS_GNU_DATE) diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index dafac618c59..89fcbc88521 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -102,6 +102,13 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_JDK_OPTIONS], CHECKING_MSG: [if we should build headless-only (no GUI)]) AC_SUBST(ENABLE_HEADLESS_ONLY) + # Avoid headless-only on macOS and Windows, it is not supported there + if test "x$ENABLE_HEADLESS_ONLY" = xtrue; then + if test "x$OPENJDK_TARGET_OS" = xwindows || test "x$OPENJDK_TARGET_OS" = xmacosx; then + AC_MSG_ERROR([headless-only is not supported on macOS and Windows]) + fi + fi + # should we linktime gc unused code sections in the JDK build ? if test "x$OPENJDK_TARGET_OS" = "xlinux"; then if test "x$OPENJDK_TARGET_CPU" = "xs390x" || test "x$OPENJDK_TARGET_CPU" = "xppc64le"; then diff --git a/make/common/modules/LauncherCommon.gmk b/make/common/modules/LauncherCommon.gmk index 7682ffbb95c..859494861b2 100644 --- a/make/common/modules/LauncherCommon.gmk +++ b/make/common/modules/LauncherCommon.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2026, 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 @@ -35,8 +35,14 @@ include ProcessMarkdown.gmk include $(TOPDIR)/make/ToolsJdk.gmk LAUNCHER_SRC := $(TOPDIR)/src/java.base/share/native/launcher + +ifeq ($(call isTargetOs, aix), true) + ADD_PLATFORM_INCLUDE_DIR := -I$(TOPDIR)/src/java.base/aix/native/include +endif + LAUNCHER_CFLAGS += -I$(TOPDIR)/src/java.base/share/native/launcher \ -I$(TOPDIR)/src/java.base/share/native/libjli \ + $(ADD_PLATFORM_INCLUDE_DIR) \ -I$(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS_TYPE)/native/libjli \ -I$(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS)/native/libjli \ # diff --git a/make/hotspot/lib/JvmFlags.gmk b/make/hotspot/lib/JvmFlags.gmk index 57b632ee532..27a96cc4865 100644 --- a/make/hotspot/lib/JvmFlags.gmk +++ b/make/hotspot/lib/JvmFlags.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2013, 2026, 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 @@ -43,10 +43,15 @@ JVM_SRC_DIRS += $(call uniq, $(wildcard $(foreach d, $(JVM_SRC_ROOTS), \ $(JVM_VARIANT_OUTPUTDIR)/gensrc # +ifeq ($(call isTargetOs, aix), true) + ADD_PLATFORM_INCLUDE_DIR := -I$(TOPDIR)/src/java.base/aix/native/include +endif + JVM_CFLAGS_INCLUDES += \ $(patsubst %,-I%,$(JVM_SRC_DIRS)) \ -I$(TOPDIR)/src/hotspot/share/include \ -I$(TOPDIR)/src/hotspot/os/$(HOTSPOT_TARGET_OS_TYPE)/include \ + $(ADD_PLATFORM_INCLUDE_DIR) \ -I$(SUPPORT_OUTPUTDIR)/modules_include/java.base \ -I$(SUPPORT_OUTPUTDIR)/modules_include/java.base/$(OPENJDK_TARGET_OS_INCLUDE_SUBDIR) \ -I$(TOPDIR)/src/java.base/share/native/libjimage \ diff --git a/make/jdk/src/classes/build/tools/intpoly/FieldGen.java b/make/jdk/src/classes/build/tools/intpoly/FieldGen.java index fcc45db0219..c29a315f368 100644 --- a/make/jdk/src/classes/build/tools/intpoly/FieldGen.java +++ b/make/jdk/src/classes/build/tools/intpoly/FieldGen.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -34,36 +34,6 @@ import java.util.*; public class FieldGen { - static FieldParams Curve25519 = new FieldParams( - "IntegerPolynomial25519", 26, 10, 1, 255, - Arrays.asList( - new Term(0, -19) - ), - Curve25519CrSequence(), simpleSmallCrSequence(10) - ); - - private static List Curve25519CrSequence() { - List result = new ArrayList(); - - // reduce(7,2) - result.add(new Reduce(17)); - result.add(new Reduce(18)); - - // carry(8,2) - result.add(new Carry(8)); - result.add(new Carry(9)); - - // reduce(0,7) - for (int i = 10; i < 17; i++) { - result.add(new Reduce(i)); - } - - // carry(0,9) - result.addAll(fullCarry(10)); - - return result; - } - static FieldParams Curve448 = new FieldParams( "IntegerPolynomial448", 28, 16, 1, 448, Arrays.asList( @@ -224,8 +194,7 @@ public class FieldGen { } static final FieldParams[] ALL_FIELDS = { - Curve25519, Curve448, - P256, P384, P521, O256, O384, O521, O25519, O448 + Curve448, P256, P384, P521, O256, O384, O521, O25519, O448 }; public static class Term { diff --git a/make/langtools/tools/previewfeature/SetupPreviewFeature.java b/make/langtools/tools/previewfeature/SetupPreviewFeature.java new file mode 100644 index 00000000000..2d5207f0e17 --- /dev/null +++ b/make/langtools/tools/previewfeature/SetupPreviewFeature.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2026, 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. + */ +package previewfeature; + +import com.sun.source.util.JavacTask; +import com.sun.source.util.Trees; +import java.io.StringWriter; +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; +import javax.lang.model.element.ElementKind; +import javax.tools.ToolProvider; + +/* Construct a hybrid PreviewFeature.Feature enum that includes constants both + * from the current JDK sources (so that they can be used in the javac API sources), + * and from the bootstrap JDK (so that they can be used in the bootstrap classfiles). + * + * This hybrid enum is only used for the interim javac. + */ +public class SetupPreviewFeature { + public static void main(String... args) throws Exception { + Class runtimeFeature = Class.forName("jdk.internal.javac.PreviewFeature$Feature"); + Set constantsToAdd = new HashSet<>(); + for (Field runtimeField : runtimeFeature.getDeclaredFields()) { + if (runtimeField.isEnumConstant()) { + constantsToAdd.add(runtimeField.getName()); + } + } + var dummy = new StringWriter(); + var compiler = ToolProvider.getSystemJavaCompiler(); + var source = Path.of(args[0]); + try (var fm = compiler.getStandardFileManager(null, null, null)) { + JavacTask task = + (JavacTask) compiler.getTask(dummy, null, null, null, null, fm.getJavaFileObjects(source)); + task.analyze(); + var sourceFeature = task.getElements() + .getTypeElement("jdk.internal.javac.PreviewFeature.Feature"); + int insertPosition = -1; + for (var el : sourceFeature.getEnclosedElements()) { + if (el.getKind() == ElementKind.ENUM_CONSTANT) { + constantsToAdd.remove(el.getSimpleName().toString()); + if (insertPosition == (-1)) { + var trees = Trees.instance(task); + var elPath = trees.getPath(el); + insertPosition = (int) trees.getSourcePositions() + .getStartPosition(elPath.getCompilationUnit(), + elPath.getLeaf()); + } + } + } + var target = Path.of(args[1]); + Files.createDirectories(target.getParent()); + if (constantsToAdd.isEmpty()) { + Files.copy(source, target); + } else { + String sourceCode = Files.readString(source); + try (var out = Files.newBufferedWriter(target)) { + out.write(sourceCode, 0, insertPosition); + out.write(constantsToAdd.stream() + .collect(Collectors.joining(", ", + "/*compatibility constants:*/ ", + ",\n"))); + out.write(sourceCode, insertPosition, sourceCode.length() - insertPosition); + } + } + } + } +} \ No newline at end of file diff --git a/make/modules/java.base/Launcher.gmk b/make/modules/java.base/Launcher.gmk index 3a3920acb12..bfae0925c07 100644 --- a/make/modules/java.base/Launcher.gmk +++ b/make/modules/java.base/Launcher.gmk @@ -95,7 +95,8 @@ ifeq ($(call isTargetOsType, unix), true) CFLAGS := $(VERSION_CFLAGS), \ EXTRA_HEADER_DIRS := libjava, \ EXTRA_OBJECT_FILES := \ - $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjava/childproc$(OBJ_SUFFIX), \ + $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjava/childproc$(OBJ_SUFFIX) \ + $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjava/childproc_errorcodes$(OBJ_SUFFIX), \ LD_SET_ORIGIN := false, \ OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/modules_libs/$(MODULE), \ )) diff --git a/make/modules/java.desktop/lib/AwtLibraries.gmk b/make/modules/java.desktop/lib/AwtLibraries.gmk index 8b6b50b9e62..887dfab01df 100644 --- a/make/modules/java.desktop/lib/AwtLibraries.gmk +++ b/make/modules/java.desktop/lib/AwtLibraries.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2026, 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 @@ -99,14 +99,16 @@ ifeq ($(call isTargetOs, windows), true) $(TOPDIR)/src/$(MODULE)/windows/native/libawt/windows/awt.rc endif -# This is the object file to provide the dladdr API, which is not -# part of AIX. It occurs several times in the jdk code base. -# Do not include it. When statically linking the java -# launcher with all JDK and VM static libraries, we use the -# --whole-archive linker option. The duplicate objects in different -# static libraries cause linking errors due to duplicate symbols. ifeq ($(call isTargetOs, aix), true) + # This is the object file to provide the dladdr API, which is not + # part of AIX. It occurs several times in the jdk code base. + # Do not include it. When statically linking the java + # launcher with all JDK and VM static libraries, we use the + # --whole-archive linker option. The duplicate objects in different + # static libraries cause linking errors due to duplicate symbols. LIBAWT_STATIC_EXCLUDE_OBJS := porting_aix.o + + LIBAWT_CFLAGS += -I$(TOPDIR)/src/java.base/aix/native/include endif # -fgcse-after-reload improves performance of MaskFill in Java2D by 20% for @@ -423,6 +425,9 @@ endif ifeq ($(call isTargetOs, linux)+$(ENABLE_HEADLESS_ONLY), true+true) LIBJAWT_CFLAGS += -DHEADLESS endif +ifeq ($(call isTargetOs, aix)+$(ENABLE_HEADLESS_ONLY), true+true) + LIBJAWT_CFLAGS += -DHEADLESS +endif ifeq ($(call isTargetOs, windows)+$(call isTargetCpu, x86), true+true) LIBJAWT_LIBS_windows := kernel32.lib diff --git a/make/modules/jdk.jpackage/Java.gmk b/make/modules/jdk.jpackage/Java.gmk index da66fc14009..1fd4d527217 100644 --- a/make/modules/jdk.jpackage/Java.gmk +++ b/make/modules/jdk.jpackage/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2026, 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 @@ -29,7 +29,7 @@ DISABLED_WARNINGS_java += dangling-doc-comments COPY += .gif .png .txt .spec .script .prerm .preinst \ .postrm .postinst .list .sh .desktop .copyright .control .plist .template \ - .icns .scpt .wxs .wxl .wxi .wxf .ico .bmp .tiff .service .xsl + .icns .scpt .wxs .wxl .wxi .wxf .ico .bmp .tiff .service .xsl .js CLEAN += .properties diff --git a/make/test/JtregNativeJdk.gmk b/make/test/JtregNativeJdk.gmk index 0482011f561..6774e708f99 100644 --- a/make/test/JtregNativeJdk.gmk +++ b/make/test/JtregNativeJdk.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, 2026, 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 @@ -63,7 +63,8 @@ ifeq ($(call isTargetOs, windows), true) BUILD_JDK_JTREG_EXCLUDE += libDirectIO.c libInheritedChannel.c \ libExplicitAttach.c libImplicitAttach.c \ exelauncher.c libFDLeaker.c exeFDLeakTester.c \ - libChangeSignalDisposition.c exePrintSignalDisposition.c + libChangeSignalDisposition.c exePrintSignalDisposition.c \ + libConcNativeFork.c libPipesCloseOnExec.c BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeNullCallerTest := $(LIBCXX) BUILD_JDK_JTREG_EXECUTABLES_LIBS_exerevokeall := advapi32.lib @@ -77,6 +78,9 @@ else BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libLinkerInvokerUnnamed := -pthread BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libLinkerInvokerModule := -pthread BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libLoaderLookupInvoker := -pthread + BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libConcNativeFork := -pthread + BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libPipesCloseOnExec := -pthread + BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libLoaderLookupInvoker := -pthread BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libExplicitAttach := -pthread BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libImplicitAttach := -pthread diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 51bb15e0f59..b79030f07e7 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -2524,10 +2524,6 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? _FLOAT_REG_mask.size() : FLOATPRESSURE; } -bool Matcher::use_asm_for_ldiv_by_con(jlong divisor) { - return false; -} - const RegMask& Matcher::divI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; @@ -8024,6 +8020,21 @@ instruct membar_release_lock() %{ ins_pipe(pipe_serial); %} +instruct membar_storeload() %{ + match(MemBarStoreLoad); + ins_cost(VOLATILE_REF_COST*100); + + format %{ "MEMBAR-store-load\n\t" + "dmb ish" %} + + ins_encode %{ + __ block_comment("membar_storeload"); + __ membar(Assembler::StoreLoad); + %} + + ins_pipe(pipe_serial); +%} + instruct unnecessary_membar_volatile() %{ predicate(unnecessary_volatile(n)); match(MemBarVolatile); @@ -8053,6 +8064,20 @@ instruct membar_volatile() %{ ins_pipe(pipe_serial); %} +instruct membar_full() %{ + match(MemBarFull); + ins_cost(VOLATILE_REF_COST*100); + + format %{ "membar_full\n\t" + "dmb ish" %} + ins_encode %{ + __ block_comment("membar_full"); + __ membar(Assembler::AnyAny); + %} + + ins_pipe(pipe_serial); +%} + // ============================================================================ // Cast/Convert Instructions diff --git a/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp b/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp index df013d9d1ee..bb6b3ce907e 100644 --- a/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp @@ -51,7 +51,6 @@ define_pd_global(bool, ProfileInterpreter, false); define_pd_global(size_t, CodeCacheExpansionSize, 32*K ); define_pd_global(size_t, CodeCacheMinBlockLength, 1); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); -define_pd_global(bool, NeverActAsServerClassMachine, true ); define_pd_global(bool, CICompileOSR, true ); #endif // !COMPILER2 define_pd_global(bool, UseTypeProfile, false); diff --git a/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp index 87ad9caaa45..192461d1a61 100644 --- a/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp @@ -74,9 +74,6 @@ define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M ); define_pd_global(size_t, CodeCacheMinBlockLength, 6); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); -// Ergonomics related flags -define_pd_global(bool, NeverActAsServerClassMachine, false); - define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed. #endif // CPU_AARCH64_C2_GLOBALS_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp b/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp index 65d448f908c..130d2949800 100644 --- a/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, Arm Limited. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -146,10 +146,10 @@ void DowncallLinker::StubGenerator::generate() { bool should_save_return_value = !_needs_return_buffer; RegSpiller out_reg_spiller(_output_registers); - int spill_offset = -1; + int out_spill_offset = -1; if (should_save_return_value) { - spill_offset = 0; + out_spill_offset = 0; // spill area can be shared with shadow space and out args, // since they are only used before the call, // and spill area is only used after. @@ -174,6 +174,9 @@ void DowncallLinker::StubGenerator::generate() { // FP-> | | // |---------------------| = frame_bottom_offset = frame_size // | (optional) | + // | in_reg_spiller area | + // |---------------------| + // | (optional) | // | capture state buf | // |---------------------| = StubLocations::CAPTURED_STATE_BUFFER // | (optional) | @@ -187,6 +190,19 @@ void DowncallLinker::StubGenerator::generate() { GrowableArray out_regs = ForeignGlobals::replace_place_holders(_input_registers, locs); ArgumentShuffle arg_shuffle(filtered_java_regs, out_regs, shuffle_reg); + // Need to spill for state capturing runtime call. + // The area spilled into is distinct from the capture state buffer. + RegSpiller in_reg_spiller(out_regs); + int in_spill_offset = -1; + if (_captured_state_mask != 0) { + // The spill area cannot be shared with the out_spill since + // spilling needs to happen before the call. Allocate a new + // region in the stack for this spill space. + in_spill_offset = allocated_frame_size; + allocated_frame_size += in_reg_spiller.spill_size_bytes(); + } + + #ifndef PRODUCT LogTarget(Trace, foreign, downcall) lt; if (lt.is_enabled()) { @@ -228,6 +244,20 @@ void DowncallLinker::StubGenerator::generate() { arg_shuffle.generate(_masm, shuffle_reg, 0, _abi._shadow_space_bytes); __ block_comment("} argument shuffle"); + if (_captured_state_mask != 0) { + assert(in_spill_offset != -1, "must be"); + __ block_comment("{ load initial thread local"); + in_reg_spiller.generate_spill(_masm, in_spill_offset); + + // Copy the contents of the capture state buffer into thread local + __ ldr(c_rarg0, Address(sp, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER))); + __ movw(c_rarg1, _captured_state_mask); + __ rt_call(CAST_FROM_FN_PTR(address, DowncallLinker::capture_state_pre), tmp1); + + in_reg_spiller.generate_fill(_masm, in_spill_offset); + __ block_comment("} load initial thread local"); + } + __ blr(as_Register(locs.get(StubLocations::TARGET_ADDRESS))); // this call is assumed not to have killed rthread @@ -254,15 +284,15 @@ void DowncallLinker::StubGenerator::generate() { __ block_comment("{ save thread local"); if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); } __ ldr(c_rarg0, Address(sp, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER))); __ movw(c_rarg1, _captured_state_mask); - __ rt_call(CAST_FROM_FN_PTR(address, DowncallLinker::capture_state), tmp1); + __ rt_call(CAST_FROM_FN_PTR(address, DowncallLinker::capture_state_post), tmp1); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); } __ block_comment("} save thread local"); @@ -321,7 +351,7 @@ void DowncallLinker::StubGenerator::generate() { if (should_save_return_value) { // Need to save the native result registers around any runtime calls. - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); } __ mov(c_rarg0, rthread); @@ -330,7 +360,7 @@ void DowncallLinker::StubGenerator::generate() { __ blr(tmp1); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); } __ b(L_after_safepoint_poll); @@ -342,13 +372,13 @@ void DowncallLinker::StubGenerator::generate() { __ bind(L_reguard); if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); } __ rt_call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages), tmp1); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); } __ b(L_after_reguard); diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp b/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp index cb53d8663ad..748ab0e0e2b 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -245,8 +245,8 @@ inline bool frame::equal(frame other) const { // Return unique id for this frame. The id must have a value where we can distinguish // identity and younger/older relationship. null represents an invalid (incomparable) -// frame. -inline intptr_t* frame::id(void) const { return unextended_sp(); } +// frame. Should not be called for heap frames. +inline intptr_t* frame::id(void) const { return real_fp(); } // Return true if the frame is older (less recent activation) than the frame represented by id inline bool frame::is_older(intptr_t* id) const { assert(this->id() != nullptr && id != nullptr, "null frame id"); @@ -412,6 +412,9 @@ inline frame frame::sender(RegisterMap* map) const { StackWatermarkSet::on_iteration(map->thread(), result); } + // Calling frame::id() is currently not supported for heap frames. + assert(result._on_heap || this->_on_heap || result.is_older(this->id()), "Must be"); + return result; } diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index 87c609be5a7..60a0ef307b5 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -1,5 +1,5 @@ // -// Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2008, 2026, 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 @@ -1112,10 +1112,6 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? 30 : FLOATPRESSURE; } -bool Matcher::use_asm_for_ldiv_by_con( jlong divisor ) { - return false; -} - // Register for DIVI projection of divmodI const RegMask& Matcher::divI_proj_mask() { ShouldNotReachHere(); @@ -4440,6 +4436,18 @@ instruct membar_release_lock() %{ ins_pipe(empty); %} +instruct membar_storeload() %{ + match(MemBarStoreLoad); + ins_cost(4*MEMORY_REF_COST); + + size(4); + format %{ "MEMBAR-storeload" %} + ins_encode %{ + __ membar(MacroAssembler::StoreLoad, noreg); + %} + ins_pipe(long_memory_op); +%} + instruct membar_volatile() %{ match(MemBarVolatile); ins_cost(4*MEMORY_REF_COST); @@ -4463,6 +4471,18 @@ instruct unnecessary_membar_volatile() %{ ins_pipe(empty); %} +instruct membar_full() %{ + match(MemBarFull); + ins_cost(4*MEMORY_REF_COST); + + size(4); + format %{ "MEMBAR-full" %} + ins_encode %{ + __ membar(MacroAssembler::StoreLoad, noreg); + %} + ins_pipe(long_memory_op); +%} + //----------Register Move Instructions----------------------------------------- // Cast Index to Pointer for unsafe natives diff --git a/src/hotspot/cpu/arm/c1_globals_arm.hpp b/src/hotspot/cpu/arm/c1_globals_arm.hpp index 992cfd0e408..9db999e81b3 100644 --- a/src/hotspot/cpu/arm/c1_globals_arm.hpp +++ b/src/hotspot/cpu/arm/c1_globals_arm.hpp @@ -52,7 +52,6 @@ define_pd_global(bool, ProfileInterpreter, false); define_pd_global(size_t, CodeCacheExpansionSize, 32*K ); define_pd_global(size_t, CodeCacheMinBlockLength, 1); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); -define_pd_global(bool, NeverActAsServerClassMachine, true); define_pd_global(bool, CICompileOSR, true ); #endif // COMPILER2 define_pd_global(bool, UseTypeProfile, false); diff --git a/src/hotspot/cpu/arm/c2_globals_arm.hpp b/src/hotspot/cpu/arm/c2_globals_arm.hpp index 84abde5650b..34da47792ae 100644 --- a/src/hotspot/cpu/arm/c2_globals_arm.hpp +++ b/src/hotspot/cpu/arm/c2_globals_arm.hpp @@ -93,7 +93,4 @@ define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed -// Ergonomics related flags -define_pd_global(bool, NeverActAsServerClassMachine, false); - #endif // CPU_ARM_C2_GLOBALS_ARM_HPP diff --git a/src/hotspot/cpu/ppc/assembler_ppc.hpp b/src/hotspot/cpu/ppc/assembler_ppc.hpp index da2daffd579..378e01fc1cc 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.hpp @@ -1580,10 +1580,6 @@ class Assembler : public AbstractAssembler { static bool is_nop(int x) { return x == 0x60000000; } - // endgroup opcode for Power6 - static bool is_endgroup(int x) { - return is_ori(x) && inv_ra_field(x) == 1 && inv_rs_field(x) == 1 && inv_d1_field(x) == 0; - } private: @@ -1659,9 +1655,6 @@ class Assembler : public AbstractAssembler { inline void ori_opt( Register d, int ui16); inline void oris_opt(Register d, int ui16); - // endgroup opcode for Power6 - inline void endgroup(); - // count instructions inline void cntlzw( Register a, Register s); inline void cntlzw_( Register a, Register s); diff --git a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp index bd6f3300606..d349bbc6f87 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp @@ -253,8 +253,6 @@ inline void Assembler::mr( Register d, Register s) { Assembler::orr(d, s, inline void Assembler::ori_opt( Register d, int ui16) { if (ui16!=0) Assembler::ori( d, d, ui16); } inline void Assembler::oris_opt(Register d, int ui16) { if (ui16!=0) Assembler::oris(d, d, ui16); } -inline void Assembler::endgroup() { Assembler::ori(R1, R1, 0); } - // count instructions inline void Assembler::cntlzw( Register a, Register s) { emit_int32(CNTLZW_OPCODE | rta(a) | rs(s) | rc(0)); } inline void Assembler::cntlzw_( Register a, Register s) { emit_int32(CNTLZW_OPCODE | rta(a) | rs(s) | rc(1)); } diff --git a/src/hotspot/cpu/ppc/c1_globals_ppc.hpp b/src/hotspot/cpu/ppc/c1_globals_ppc.hpp index 7d2e44a88b6..c6fe15aac07 100644 --- a/src/hotspot/cpu/ppc/c1_globals_ppc.hpp +++ b/src/hotspot/cpu/ppc/c1_globals_ppc.hpp @@ -51,7 +51,6 @@ define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M ); define_pd_global(size_t, CodeCacheExpansionSize, 32*K); define_pd_global(size_t, CodeCacheMinBlockLength, 1); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); -define_pd_global(bool, NeverActAsServerClassMachine, true); define_pd_global(size_t, InitialCodeCacheSize, 160*K); #endif // !COMPILER2 diff --git a/src/hotspot/cpu/ppc/c2_globals_ppc.hpp b/src/hotspot/cpu/ppc/c2_globals_ppc.hpp index eeff8d93dd4..e4942fa1850 100644 --- a/src/hotspot/cpu/ppc/c2_globals_ppc.hpp +++ b/src/hotspot/cpu/ppc/c2_globals_ppc.hpp @@ -90,7 +90,4 @@ define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); define_pd_global(bool, TrapBasedRangeChecks, true); -// Ergonomics related flags -define_pd_global(bool, NeverActAsServerClassMachine, false); - #endif // CPU_PPC_C2_GLOBALS_PPC_HPP diff --git a/src/hotspot/cpu/ppc/disassembler_ppc.cpp b/src/hotspot/cpu/ppc/disassembler_ppc.cpp index fb3cb50cdec..2e16e1a301f 100644 --- a/src/hotspot/cpu/ppc/disassembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/disassembler_ppc.cpp @@ -119,9 +119,6 @@ address Disassembler::decode_instruction0(address here, outputStream * st, addre } else if (instruction == 0xbadbabe) { st->print(".data 0xbadbabe"); next = here + Assembler::instr_len(here); - } else if (Assembler::is_endgroup(instruction)) { - st->print("endgroup"); - next = here + Assembler::instr_len(here); } else { next = here; } diff --git a/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp b/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp index f12d25ac611..d149fc33ac3 100644 --- a/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp +++ b/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, 2025 SAP SE. All rights reserved. - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -135,10 +135,10 @@ void DowncallLinker::StubGenerator::generate() { bool should_save_return_value = !_needs_return_buffer; RegSpiller out_reg_spiller(_output_registers); - int spill_offset = -1; + int out_spill_offset = -1; if (should_save_return_value) { - spill_offset = frame::native_abi_reg_args_size; + out_spill_offset = frame::native_abi_reg_args_size; // Spill area can be shared with additional out args (>8), // since it is only used after the call. int frame_size_including_spill_area = frame::native_abi_reg_args_size + out_reg_spiller.spill_size_bytes(); @@ -170,6 +170,18 @@ void DowncallLinker::StubGenerator::generate() { ArgumentShuffle arg_shuffle(filtered_java_regs, out_regs, _abi._scratch1); + // Need to spill for state capturing runtime call. + // The area spilled into is distinct from the capture state buffer. + RegSpiller in_reg_spiller(out_regs); + int in_spill_offset = -1; + if (_captured_state_mask != 0) { + // The spill area cannot be shared with the out_spill since + // spilling needs to happen before the call. Allocate a new + // region in the stack for this spill space. + in_spill_offset = allocated_frame_size; + allocated_frame_size += in_reg_spiller.spill_size_bytes(); + } + #ifndef PRODUCT LogTarget(Trace, foreign, downcall) lt; if (lt.is_enabled()) { @@ -211,6 +223,21 @@ void DowncallLinker::StubGenerator::generate() { arg_shuffle.generate(_masm, as_VMStorage(callerSP), frame::jit_out_preserve_size, frame::native_abi_minframe_size); __ block_comment("} argument shuffle"); + if (_captured_state_mask != 0) { + assert(in_spill_offset != -1, "must be"); + __ block_comment("{ load initial thread local"); + in_reg_spiller.generate_spill(_masm, in_spill_offset); + + // Copy the contents of the capture state buffer into thread local + __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, DowncallLinker::capture_state_pre), R0); + __ ld(R3_ARG1, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER), R1_SP); + __ load_const_optimized(R4_ARG2, _captured_state_mask, R0); + __ call_c(call_target_address); + + in_reg_spiller.generate_fill(_masm, in_spill_offset); + __ block_comment("} load initial thread local"); + } + __ call_c(call_target_address); if (_needs_return_buffer) { @@ -247,16 +274,16 @@ void DowncallLinker::StubGenerator::generate() { __ block_comment("{ save thread local"); if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); } - __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, DowncallLinker::capture_state), R0); + __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, DowncallLinker::capture_state_post), R0); __ ld(R3_ARG1, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER), R1_SP); __ load_const_optimized(R4_ARG2, _captured_state_mask, R0); __ call_c(call_target_address); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); } __ block_comment("} save thread local"); @@ -310,7 +337,7 @@ void DowncallLinker::StubGenerator::generate() { if (should_save_return_value) { // Need to save the native result registers around any runtime calls. - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); } __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, JavaThread::check_special_condition_for_native_trans), R0); @@ -318,7 +345,7 @@ void DowncallLinker::StubGenerator::generate() { __ call_c(call_target_address); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); } __ b(L_after_safepoint_poll); @@ -330,14 +357,14 @@ void DowncallLinker::StubGenerator::generate() { __ bind(L_reguard); if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); } __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, SharedRuntime::reguard_yellow_pages), R0); __ call_c(call_target_address); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); } __ b(L_after_reguard); diff --git a/src/hotspot/cpu/ppc/frame_ppc.inline.hpp b/src/hotspot/cpu/ppc/frame_ppc.inline.hpp index bb711f2d053..3c05f950d0c 100644 --- a/src/hotspot/cpu/ppc/frame_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/frame_ppc.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -137,10 +137,10 @@ inline frame::frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address // Return unique id for this frame. The id must have a value where we // can distinguish identity and younger/older relationship. null -// represents an invalid (incomparable) frame. +// represents an invalid (incomparable) frame. Should not be called for heap frames. inline intptr_t* frame::id(void) const { // Use _fp. _sp or _unextended_sp wouldn't be correct due to resizing. - return _fp; + return real_fp(); } // Return true if this frame is older (less recent activation) than @@ -319,6 +319,9 @@ inline frame frame::sender(RegisterMap* map) const { StackWatermarkSet::on_iteration(map->thread(), result); } + // Calling frame::id() is currently not supported for heap frames. + assert(result._on_heap || this->_on_heap || result.is_older(this->id()), "Must be"); + return result; } diff --git a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp index 82d06f6c685..3692b247989 100644 --- a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2018, 2025 SAP SE. All rights reserved. + * Copyright (c) 2018, 2026 SAP SE. 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 @@ -179,6 +179,11 @@ void BarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, Re __ ld(dst, 0, dst); // Resolve (untagged) jobject. } +void BarrierSetAssembler::try_resolve_weak_handle(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) { + // Load the oop from the weak handle. + __ ld(obj, 0, obj); +} + void BarrierSetAssembler::nmethod_entry_barrier(MacroAssembler* masm, Register tmp) { BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod(); assert_different_registers(tmp, R0); @@ -275,11 +280,6 @@ OptoReg::Name BarrierSetAssembler::refine_register(const Node* node, OptoReg::Na return opto_reg; } -void BarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) { - // Load the oop from the weak handle. - __ ld(obj, 0, obj); -} - #undef __ #define __ _masm-> diff --git a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp index d78071f2ee0..8112542d761 100644 --- a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2018, 2022 SAP SE. All rights reserved. + * Copyright (c) 2018, 2026 SAP SE. 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 @@ -70,6 +70,12 @@ public: virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register dst, Register jni_env, Register obj, Register tmp, Label& slowpath); + // Can be used in nmethods including native wrappers. + // Attention: obj will only be valid until next safepoint (no SATB barrier). + // TODO: maybe rename to try_peek_weak_handle on all platforms (try: operation may fail, peek: obj is not kept alive) + // (other platforms currently use it for C2 only: try_resolve_weak_handle_in_c2) + virtual void try_resolve_weak_handle(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path); + virtual void barrier_stubs_init() {} virtual NMethodPatchingType nmethod_patching_type() { return NMethodPatchingType::stw_instruction_and_data_patch; } @@ -81,8 +87,6 @@ public: #ifdef COMPILER2 OptoReg::Name refine_register(const Node* node, OptoReg::Name opto_reg) const; - virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, - Register tmp, Label& slow_path); #endif // COMPILER2 }; diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp index e1f0416d65d..8e99d23cc99 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2025, Red Hat, Inc. All rights reserved. - * Copyright (c) 2012, 2025 SAP SE. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. 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 @@ -663,17 +663,16 @@ void ShenandoahBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler __ block_comment("} try_resolve_jobject_in_native (shenandoahgc)"); } -#ifdef COMPILER2 -void ShenandoahBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler *masm, Register obj, - Register tmp, Label &slow_path) { - __ block_comment("try_resolve_weak_handle_in_c2 (shenandoahgc) {"); +void ShenandoahBarrierSetAssembler::try_resolve_weak_handle(MacroAssembler *masm, Register obj, + Register tmp, Label &slow_path) { + __ block_comment("try_resolve_weak_handle (shenandoahgc) {"); assert_different_registers(obj, tmp); Label done; // Resolve weak handle using the standard implementation. - BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, tmp, slow_path); + BarrierSetAssembler::try_resolve_weak_handle(masm, obj, tmp, slow_path); // Check if the reference is null, and if it is, take the fast path. __ cmpdi(CR0, obj, 0); @@ -686,9 +685,8 @@ void ShenandoahBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler __ bne(CR0, slow_path); __ bind(done); - __ block_comment("} try_resolve_weak_handle_in_c2 (shenandoahgc)"); + __ block_comment("} try_resolve_weak_handle (shenandoahgc)"); } -#endif // Special shenandoah CAS implementation that handles false negatives due // to concurrent evacuation. That is, the CAS operation is intended to succeed in diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp index 672f8122bcb..58180c49642 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2022, Red Hat, Inc. All rights reserved. - * Copyright (c) 2012, 2022 SAP SE. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. 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 @@ -122,9 +122,8 @@ public: virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register dst, Register jni_env, Register obj, Register tmp, Label& slowpath); -#ifdef COMPILER2 - virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path); -#endif + + virtual void try_resolve_weak_handle(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path); }; #endif // CPU_PPC_GC_SHENANDOAH_SHENANDOAHBARRIERSETASSEMBLER_PPC_HPP diff --git a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp index bfa3c87c179..3e74dfb88cb 100644 --- a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2021, 2025 SAP SE. All rights reserved. + * Copyright (c) 2021, 2026 SAP SE. 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 @@ -627,6 +627,19 @@ void ZBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, R __ block_comment("} try_resolve_jobject_in_native (zgc)"); } +void ZBarrierSetAssembler::try_resolve_weak_handle(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) { + // Resolve weak handle using the standard implementation. + BarrierSetAssembler::try_resolve_weak_handle(masm, obj, tmp, slow_path); + + // Check if the oop is bad, in which case we need to take the slow path. + __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatMarkBadMask); + __ andi_(R0, obj, barrier_Relocation::unpatched); + __ bne(CR0, slow_path); + + // Oop is okay, so we uncolor it. + __ srdi(obj, obj, ZPointerLoadShift); +} + #undef __ #ifdef COMPILER1 @@ -950,19 +963,6 @@ void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm, __ b(*stub->continuation()); } -void ZBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) { - // Resolve weak handle using the standard implementation. - BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, tmp, slow_path); - - // Check if the oop is bad, in which case we need to take the slow path. - __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatMarkBadMask); - __ andi_(R0, obj, barrier_Relocation::unpatched); - __ bne(CR0, slow_path); - - // Oop is okay, so we uncolor it. - __ srdi(obj, obj, ZPointerLoadShift); -} - #undef __ #endif // COMPILER2 diff --git a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp index e31817370d9..655184cf6a3 100644 --- a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2021, 2022 SAP SE. All rights reserved. + * Copyright (c) 2021, 2026 SAP SE. 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 @@ -72,6 +72,8 @@ public: virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register dst, Register jni_env, Register obj, Register tmp, Label& slowpath); + virtual void try_resolve_weak_handle(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path); + virtual void check_oop(MacroAssembler *masm, Register obj, const char* msg); virtual NMethodPatchingType nmethod_patching_type() { return NMethodPatchingType::conc_instruction_and_data_patch; } @@ -108,8 +110,6 @@ public: void generate_c2_load_barrier_stub(MacroAssembler* masm, ZLoadBarrierStubC2* stub) const; void generate_c2_store_barrier_stub(MacroAssembler* masm, ZStoreBarrierStubC2* stub) const; - - void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path); #endif // COMPILER2 void store_barrier_fast(MacroAssembler* masm, diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp index 986dd335816..14e90ddf185 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2025 SAP SE. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. 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 @@ -483,7 +483,7 @@ void MacroAssembler::set_dest_of_bc_far_at(address instruction_addr, address des // variant 3, far cond branch to the next instruction, already patched to nops: // // nop - // endgroup + // nop // SKIP/DEST: // return; @@ -500,7 +500,7 @@ void MacroAssembler::set_dest_of_bc_far_at(address instruction_addr, address des if (is_bc_far_variant2_at(instruction_addr) && dest == instruction_addr + 8) { // Far branch to next instruction: Optimize it by patching nops (produce variant 3). masm.nop(); - masm.endgroup(); + masm.nop(); } else { if (is_bc_far_variant1_at(instruction_addr)) { // variant 1, the 1st instruction contains the destination address: @@ -2800,7 +2800,7 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register // Check if object matches. ld(tmp3, in_bytes(ObjectMonitor::object_offset()), monitor); BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); - bs_asm->try_resolve_weak_handle_in_c2(this, tmp3, tmp2, slow_path); + bs_asm->try_resolve_weak_handle(this, tmp3, tmp2, slow_path); cmpd(CR0, tmp3, obj); bne(CR0, slow_path); diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp index 58dec702d7a..4be62098bdf 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp @@ -70,14 +70,6 @@ class MacroAssembler: public Assembler { // Move register if destination register and target register are different inline void mr_if_needed(Register rd, Register rs, bool allow_invalid = false); inline void fmr_if_needed(FloatRegister rd, FloatRegister rs); - // This is dedicated for emitting scheduled mach nodes. For better - // readability of the ad file I put it here. - // Endgroups are not needed if - // - the scheduler is off - // - the scheduler found that there is a natural group end, in that - // case it reduced the size of the instruction used in the test - // yielding 'needed'. - inline void endgroup_if_needed(bool needed); // Memory barriers. inline void membar(int bits); diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.inline.hpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.inline.hpp index 2b19d84c69c..cdeb8527bea 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.inline.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2025 SAP SE. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. 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 @@ -72,11 +72,6 @@ inline void MacroAssembler::mr_if_needed(Register rd, Register rs, bool allow_no inline void MacroAssembler::fmr_if_needed(FloatRegister rd, FloatRegister rs) { if (rs != rd) fmr(rd, rs); } -inline void MacroAssembler::endgroup_if_needed(bool needed) { - if (needed) { - endgroup(); - } -} inline void MacroAssembler::membar(int bits) { // Comment: Usage of elemental_membar(bits) is not recommended for Power 8. @@ -240,13 +235,13 @@ inline bool MacroAssembler::is_bc_far_variant3_at(address instruction_addr) { // Variant 3, far cond branch to the next instruction, already patched to nops: // // nop - // endgroup + // nop // SKIP/DEST: // const int instruction_1 = *(int*)(instruction_addr); const int instruction_2 = *(int*)(instruction_addr + 4); return is_nop(instruction_1) && - is_endgroup(instruction_2); + is_nop(instruction_2); } // set dst to -1, 0, +1 as follows: if CR0bi is "greater than", dst is set to 1, diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 7e3cd04171d..f3d33b4305d 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -2457,10 +2457,6 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? 28 : FLOATPRESSURE; } -bool Matcher::use_asm_for_ldiv_by_con(jlong divisor) { - return false; -} - // Register for DIVI projection of divmodI. const RegMask& Matcher::divI_proj_mask() { ShouldNotReachHere(); @@ -6327,36 +6323,8 @@ instruct loadConD_Ex(regD dst, immD src) %{ // Prefetch instructions. // Must be safe to execute with invalid address (cannot fault). -// Special prefetch versions which use the dcbz instruction. -instruct prefetch_alloc_zero(indirectMemory mem, iRegLsrc src) %{ - match(PrefetchAllocation (AddP mem src)); - predicate(AllocatePrefetchStyle == 3); - ins_cost(MEMORY_REF_COST); - - format %{ "PREFETCH $mem, 2, $src \t// Prefetch write-many with zero" %} - size(4); - ins_encode %{ - __ dcbz($src$$Register, $mem$$base$$Register); - %} - ins_pipe(pipe_class_memory); -%} - -instruct prefetch_alloc_zero_no_offset(indirectMemory mem) %{ - match(PrefetchAllocation mem); - predicate(AllocatePrefetchStyle == 3); - ins_cost(MEMORY_REF_COST); - - format %{ "PREFETCH $mem, 2 \t// Prefetch write-many with zero" %} - size(4); - ins_encode %{ - __ dcbz($mem$$base$$Register); - %} - ins_pipe(pipe_class_memory); -%} - instruct prefetch_alloc(indirectMemory mem, iRegLsrc src) %{ match(PrefetchAllocation (AddP mem src)); - predicate(AllocatePrefetchStyle != 3); ins_cost(MEMORY_REF_COST); format %{ "PREFETCH $mem, 2, $src \t// Prefetch write-many" %} @@ -6369,7 +6337,6 @@ instruct prefetch_alloc(indirectMemory mem, iRegLsrc src) %{ instruct prefetch_alloc_no_offset(indirectMemory mem) %{ match(PrefetchAllocation mem); - predicate(AllocatePrefetchStyle != 3); ins_cost(MEMORY_REF_COST); format %{ "PREFETCH $mem, 2 \t// Prefetch write-many" %} @@ -7163,6 +7130,18 @@ instruct membar_release_lock() %{ ins_pipe(pipe_class_default); %} +instruct membar_storeload() %{ + match(MemBarStoreLoad); + ins_cost(4*MEMORY_REF_COST); + + format %{ "MEMBAR-store-load" %} + size(4); + ins_encode %{ + __ fence(); + %} + ins_pipe(pipe_class_default); +%} + instruct membar_volatile() %{ match(MemBarVolatile); ins_cost(4*MEMORY_REF_COST); @@ -7205,6 +7184,18 @@ instruct membar_volatile() %{ // ins_pipe(pipe_class_default); //%} +instruct membar_full() %{ + match(MemBarFull); + ins_cost(4*MEMORY_REF_COST); + + format %{ "MEMBAR-full" %} + size(4); + ins_encode %{ + __ fence(); + %} + ins_pipe(pipe_class_default); +%} + instruct membar_CPUOrder() %{ match(MemBarCPUOrder); ins_cost(0); @@ -10312,7 +10303,7 @@ instruct cmovI_bso_stackSlotL(iRegIdst dst, flagsRegSrc crx, stackSlotL src) %{ ins_variable_size_depending_on_alignment(true); - format %{ "cmovI $crx, $dst, $src" %} + format %{ "CMOVI $crx, $dst, $src" %} size(8); ins_encode( enc_cmove_bso_stackSlotL(dst, crx, src) ); ins_pipe(pipe_class_default); @@ -10325,7 +10316,7 @@ instruct cmovI_bso_reg(iRegIdst dst, flagsRegSrc crx, regD src) %{ ins_variable_size_depending_on_alignment(true); - format %{ "cmovI $crx, $dst, $src" %} + format %{ "CMOVI $crx, $dst, $src" %} size(8); ins_encode( enc_cmove_bso_reg(dst, crx, src) ); ins_pipe(pipe_class_default); @@ -10337,7 +10328,7 @@ instruct cmovI_bso_reg_conLvalue0_Ex(iRegIdst dst, flagsRegSrc crx, regD src) %{ effect(DEF dst, USE crx, USE src); predicate(false); - format %{ "CmovI $dst, $crx, $src \t// postalloc expanded" %} + format %{ "CMOVI $dst, $crx, $src \t// postalloc expanded" %} postalloc_expand %{ // // replaces @@ -10487,7 +10478,7 @@ instruct cmovL_bso_stackSlotL(iRegLdst dst, flagsRegSrc crx, stackSlotL src) %{ ins_variable_size_depending_on_alignment(true); - format %{ "cmovL $crx, $dst, $src" %} + format %{ "CMOVL $crx, $dst, $src" %} size(8); ins_encode( enc_cmove_bso_stackSlotL(dst, crx, src) ); ins_pipe(pipe_class_default); @@ -10500,7 +10491,7 @@ instruct cmovL_bso_reg(iRegLdst dst, flagsRegSrc crx, regD src) %{ ins_variable_size_depending_on_alignment(true); - format %{ "cmovL $crx, $dst, $src" %} + format %{ "CMOVL $crx, $dst, $src" %} size(8); ins_encode( enc_cmove_bso_reg(dst, crx, src) ); ins_pipe(pipe_class_default); @@ -10512,7 +10503,7 @@ instruct cmovL_bso_reg_conLvalue0_Ex(iRegLdst dst, flagsRegSrc crx, regD src) %{ effect(DEF dst, USE crx, USE src); predicate(false); - format %{ "CmovL $dst, $crx, $src \t// postalloc expanded" %} + format %{ "CMOVL $dst, $crx, $src \t// postalloc expanded" %} postalloc_expand %{ // // replaces @@ -10713,9 +10704,9 @@ instruct convF2HF_reg_reg(iRegIdst dst, regF src, regF tmp) %{ effect(TEMP tmp); ins_cost(3 * DEFAULT_COST); size(12); - format %{ "xscvdphp $tmp, $src\t# convert to half precision\n\t" - "mffprd $dst, $tmp\t# move result from $tmp to $dst\n\t" - "extsh $dst, $dst\t# make it a proper short" + format %{ "XSCVDPHP $tmp, $src\t# convert to half precision\n\t" + "MFFPRD $dst, $tmp\t# move result from $tmp to $dst\n\t" + "EXTSH $dst, $dst\t# make it a proper short" %} ins_encode %{ __ f2hf($dst$$Register, $src$$FloatRegister, $tmp$$FloatRegister); @@ -10727,8 +10718,8 @@ instruct convHF2F_reg_reg(regF dst, iRegIsrc src) %{ match(Set dst (ConvHF2F src)); ins_cost(2 * DEFAULT_COST); size(8); - format %{ "mtfprd $dst, $src\t# move source from $src to $dst\n\t" - "xscvhpdp $dst, $dst\t# convert from half precision" + format %{ "MTFPRD $dst, $src\t# move source from $src to $dst\n\t" + "XSCVHPDP $dst, $dst\t# convert from half precision" %} ins_encode %{ __ hf2f($dst$$FloatRegister, $src$$Register); @@ -11126,7 +11117,7 @@ instruct cmov_bns_less(flagsReg crx) %{ ins_variable_size_depending_on_alignment(true); - format %{ "cmov $crx" %} + format %{ "CMOV $crx" %} size(12); ins_encode %{ Label done; @@ -11154,7 +11145,7 @@ instruct cmpF_reg_reg_Ex(flagsReg crx, regF src1, regF src2) %{ match(Set crx (CmpF src1 src2)); ins_cost(DEFAULT_COST+BRANCH_COST); - format %{ "CmpF $crx, $src1, $src2 \t// postalloc expanded" %} + format %{ "CMPF $crx, $src1, $src2 \t// postalloc expanded" %} postalloc_expand %{ // // replaces @@ -12312,7 +12303,7 @@ instruct minF(regF dst, regF src1, regF src2) %{ predicate(PowerArchitecturePPC64 >= 9); ins_cost(DEFAULT_COST); - format %{ "MinF $dst, $src1, $src2" %} + format %{ "XSMINJDP $dst, $src1, $src2\t// MinF" %} size(4); ins_encode %{ __ xsminjdp($dst$$FloatRegister->to_vsr(), $src1$$FloatRegister->to_vsr(), $src2$$FloatRegister->to_vsr()); @@ -12325,7 +12316,7 @@ instruct minD(regD dst, regD src1, regD src2) %{ predicate(PowerArchitecturePPC64 >= 9); ins_cost(DEFAULT_COST); - format %{ "MinD $dst, $src1, $src2" %} + format %{ "XSMINJDP $dst, $src1, $src2\t// MinD" %} size(4); ins_encode %{ __ xsminjdp($dst$$FloatRegister->to_vsr(), $src1$$FloatRegister->to_vsr(), $src2$$FloatRegister->to_vsr()); @@ -12338,7 +12329,7 @@ instruct maxF(regF dst, regF src1, regF src2) %{ predicate(PowerArchitecturePPC64 >= 9); ins_cost(DEFAULT_COST); - format %{ "MaxF $dst, $src1, $src2" %} + format %{ "XSMAXJDP $dst, $src1, $src2\t// MaxF" %} size(4); ins_encode %{ __ xsmaxjdp($dst$$FloatRegister->to_vsr(), $src1$$FloatRegister->to_vsr(), $src2$$FloatRegister->to_vsr()); @@ -12351,7 +12342,7 @@ instruct maxD(regD dst, regD src1, regD src2) %{ predicate(PowerArchitecturePPC64 >= 9); ins_cost(DEFAULT_COST); - format %{ "MaxD $dst, $src1, $src2" %} + format %{ "XSMAXJDP $dst, $src1, $src2\t// MaxD" %} size(4); ins_encode %{ __ xsmaxjdp($dst$$FloatRegister->to_vsr(), $src1$$FloatRegister->to_vsr(), $src2$$FloatRegister->to_vsr()); @@ -13881,7 +13872,7 @@ instruct vfma2D_neg2(vecX dst, vecX src1, vecX src2) %{ instruct overflowAddL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{ match(Set cr0 (OverflowAddL op1 op2)); - format %{ "add_ $op1, $op2\t# overflow check long" %} + format %{ "ADD_ $op1, $op2\t# overflow check long" %} size(12); ins_encode %{ __ li(R0, 0); @@ -13894,7 +13885,7 @@ instruct overflowAddL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{ instruct overflowSubL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{ match(Set cr0 (OverflowSubL op1 op2)); - format %{ "subfo_ R0, $op2, $op1\t# overflow check long" %} + format %{ "SUBFO_ R0, $op2, $op1\t# overflow check long" %} size(12); ins_encode %{ __ li(R0, 0); @@ -13907,7 +13898,7 @@ instruct overflowSubL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{ instruct overflowNegL_reg(flagsRegCR0 cr0, immL_0 zero, iRegLsrc op2) %{ match(Set cr0 (OverflowSubL zero op2)); - format %{ "nego_ R0, $op2\t# overflow check long" %} + format %{ "NEGO_ R0, $op2\t# overflow check long" %} size(12); ins_encode %{ __ li(R0, 0); @@ -13920,7 +13911,7 @@ instruct overflowNegL_reg(flagsRegCR0 cr0, immL_0 zero, iRegLsrc op2) %{ instruct overflowMulL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{ match(Set cr0 (OverflowMulL op1 op2)); - format %{ "mulldo_ R0, $op1, $op2\t# overflow check long" %} + format %{ "MULLDO_ R0, $op1, $op2\t# overflow check long" %} size(12); ins_encode %{ __ li(R0, 0); @@ -14297,7 +14288,7 @@ instruct ForwardExceptionjmp() match(ForwardException); ins_cost(CALL_COST); - format %{ "Jmp forward_exception_stub" %} + format %{ "JMP forward_exception_stub" %} ins_encode %{ __ set_inst_mark(); __ b64_patchable(StubRoutines::forward_exception_entry(), relocInfo::runtime_call_type); @@ -14325,7 +14316,7 @@ instruct RethrowException() %{ match(Rethrow); ins_cost(CALL_COST); - format %{ "Jmp rethrow_stub" %} + format %{ "JMP rethrow_stub" %} ins_encode %{ __ set_inst_mark(); __ b64_patchable((address)OptoRuntime::rethrow_stub(), relocInfo::runtime_call_type); @@ -14367,20 +14358,6 @@ instruct tlsLoadP(threadRegP dst) %{ //---Some PPC specific nodes--------------------------------------------------- -// Stop a group. -instruct endGroup() %{ - ins_cost(0); - - ins_is_nop(true); - - format %{ "End Bundle (ori r1, r1, 0)" %} - size(4); - ins_encode %{ - __ endgroup(); - %} - ins_pipe(pipe_class_default); -%} - // Nop instructions instruct fxNop() %{ diff --git a/src/hotspot/cpu/riscv/c1_globals_riscv.hpp b/src/hotspot/cpu/riscv/c1_globals_riscv.hpp index 00b92a0dec3..b940393f063 100644 --- a/src/hotspot/cpu/riscv/c1_globals_riscv.hpp +++ b/src/hotspot/cpu/riscv/c1_globals_riscv.hpp @@ -51,7 +51,6 @@ define_pd_global(bool, ProfileInterpreter, false); define_pd_global(size_t, CodeCacheExpansionSize, 32*K ); define_pd_global(size_t, CodeCacheMinBlockLength, 1); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); -define_pd_global(bool, NeverActAsServerClassMachine, true ); define_pd_global(bool, CICompileOSR, true ); #endif // !COMPILER2 define_pd_global(bool, UseTypeProfile, false); diff --git a/src/hotspot/cpu/riscv/c2_globals_riscv.hpp b/src/hotspot/cpu/riscv/c2_globals_riscv.hpp index afb4ae8fcdd..73ef97939ed 100644 --- a/src/hotspot/cpu/riscv/c2_globals_riscv.hpp +++ b/src/hotspot/cpu/riscv/c2_globals_riscv.hpp @@ -74,9 +74,6 @@ define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M ); define_pd_global(size_t, CodeCacheMinBlockLength, 6); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); -// Ergonomics related flags -define_pd_global(bool, NeverActAsServerClassMachine, false); - define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed. #endif // CPU_RISCV_C2_GLOBALS_RISCV_HPP diff --git a/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp b/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp index cc685645ec5..f9d7ce78ff0 100644 --- a/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp +++ b/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -140,10 +140,10 @@ void DowncallLinker::StubGenerator::generate() { bool should_save_return_value = !_needs_return_buffer; RegSpiller out_reg_spiller(_output_registers); - int spill_offset = -1; + int out_spill_offset = -1; if (should_save_return_value) { - spill_offset = 0; + out_spill_offset = 0; // spill area can be shared with shadow space and out args, // since they are only used before the call, // and spill area is only used after. @@ -168,6 +168,9 @@ void DowncallLinker::StubGenerator::generate() { // FP-> | | // |---------------------| = frame_bottom_offset = frame_size // | (optional) | + // | in_reg_spiller area | + // |---------------------| + // | (optional) | // | capture state buf | // |---------------------| = StubLocations::CAPTURED_STATE_BUFFER // | (optional) | @@ -181,6 +184,18 @@ void DowncallLinker::StubGenerator::generate() { GrowableArray out_regs = ForeignGlobals::replace_place_holders(_input_registers, locs); ArgumentShuffle arg_shuffle(filtered_java_regs, out_regs, shuffle_reg); + // Need to spill for state capturing runtime call. + // The area spilled into is distinct from the capture state buffer. + RegSpiller in_reg_spiller(out_regs); + int in_spill_offset = -1; + if (_captured_state_mask != 0) { + // The spill area cannot be shared with the out_spill since + // spilling needs to happen before the call. Allocate a new + // region in the stack for this spill space. + in_spill_offset = allocated_frame_size; + allocated_frame_size += in_reg_spiller.spill_size_bytes(); + } + #ifndef PRODUCT LogTarget(Trace, foreign, downcall) lt; if (lt.is_enabled()) { @@ -226,6 +241,20 @@ void DowncallLinker::StubGenerator::generate() { arg_shuffle.generate(_masm, shuffle_reg, 0, _abi._shadow_space_bytes); __ block_comment("} argument shuffle"); + if (_captured_state_mask != 0) { + assert(in_spill_offset != -1, "must be"); + __ block_comment("{ load initial thread local"); + in_reg_spiller.generate_spill(_masm, in_spill_offset); + + // Copy the contents of the capture state buffer into thread local + __ ld(c_rarg0, Address(sp, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER))); + __ mv(c_rarg1, _captured_state_mask); + __ rt_call(CAST_FROM_FN_PTR(address, DowncallLinker::capture_state_pre)); + + in_reg_spiller.generate_fill(_masm, in_spill_offset); + __ block_comment("} load initial thread local"); + } + __ jalr(as_Register(locs.get(StubLocations::TARGET_ADDRESS))); // this call is assumed not to have killed xthread @@ -254,15 +283,15 @@ void DowncallLinker::StubGenerator::generate() { __ block_comment("{ save thread local"); if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); } __ ld(c_rarg0, Address(sp, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER))); __ mv(c_rarg1, _captured_state_mask); - __ rt_call(CAST_FROM_FN_PTR(address, DowncallLinker::capture_state)); + __ rt_call(CAST_FROM_FN_PTR(address, DowncallLinker::capture_state_post)); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); } __ block_comment("} save thread local"); @@ -319,7 +348,7 @@ void DowncallLinker::StubGenerator::generate() { if (should_save_return_value) { // Need to save the native result registers around any runtime calls. - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); } __ mv(c_rarg0, xthread); @@ -327,7 +356,7 @@ void DowncallLinker::StubGenerator::generate() { __ rt_call(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans)); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); } __ j(L_after_safepoint_poll); __ block_comment("} L_safepoint_poll_slow_path"); @@ -339,13 +368,13 @@ void DowncallLinker::StubGenerator::generate() { if (should_save_return_value) { // Need to save the native result registers around any runtime calls. - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); } __ rt_call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages)); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); } __ j(L_after_reguard); diff --git a/src/hotspot/cpu/riscv/frame_riscv.inline.hpp b/src/hotspot/cpu/riscv/frame_riscv.inline.hpp index 51a203c548c..d1841a347e9 100644 --- a/src/hotspot/cpu/riscv/frame_riscv.inline.hpp +++ b/src/hotspot/cpu/riscv/frame_riscv.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -236,8 +236,8 @@ inline bool frame::equal(frame other) const { // Return unique id for this frame. The id must have a value where we can distinguish // identity and younger/older relationship. null represents an invalid (incomparable) -// frame. -inline intptr_t* frame::id(void) const { return unextended_sp(); } +// frame. Should not be called for heap frames. +inline intptr_t* frame::id(void) const { return real_fp(); } // Return true if the frame is older (less recent activation) than the frame represented by id inline bool frame::is_older(intptr_t* id) const { assert(this->id() != nullptr && id != nullptr, "null frame id"); @@ -398,6 +398,9 @@ frame frame::sender(RegisterMap* map) const { StackWatermarkSet::on_iteration(map->thread(), result); } + // Calling frame::id() is currently not supported for heap frames. + assert(result._on_heap || this->_on_heap || result.is_older(this->id()), "Must be"); + return result; } diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 54ea81683fc..54c0d9c0955 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -2111,10 +2111,6 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? _FLOAT_REG_mask.size() : FLOATPRESSURE; } -bool Matcher::use_asm_for_ldiv_by_con(jlong divisor) { - return false; -} - const RegMask& Matcher::divI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; @@ -8156,6 +8152,22 @@ instruct unnecessary_membar_rvtso() %{ ins_pipe(real_empty); %} +instruct membar_storeload_rvtso() %{ + predicate(UseZtso); + match(MemBarStoreLoad); + ins_cost(VOLATILE_REF_COST); + + format %{ "#@membar_storeload_rvtso\n\t" + "fence w, r"%} + + ins_encode %{ + __ block_comment("membar_storeload_rvtso"); + __ membar(MacroAssembler::StoreLoad); + %} + + ins_pipe(pipe_slow); +%} + instruct membar_volatile_rvtso() %{ predicate(UseZtso); match(MemBarVolatile); @@ -8186,6 +8198,22 @@ instruct unnecessary_membar_volatile_rvtso() %{ ins_pipe(real_empty); %} +instruct membar_full_rvtso() %{ + predicate(UseZtso); + match(MemBarFull); + ins_cost(VOLATILE_REF_COST); + + format %{ "#@membar_full_rvtso\n\t" + "fence rw, rw" %} + + ins_encode %{ + __ block_comment("membar_full_rvtso"); + __ membar(MacroAssembler::AnyAny); + %} + + ins_pipe(pipe_slow); +%} + // RVWMO instruct membar_aqcuire_rvwmo() %{ @@ -8235,6 +8263,22 @@ instruct membar_storestore_rvwmo() %{ ins_pipe(pipe_serial); %} +instruct membar_storeload_rvwmo() %{ + predicate(!UseZtso); + match(MemBarStoreLoad); + ins_cost(VOLATILE_REF_COST); + + format %{ "#@membar_storeload_rvwmo\n\t" + "fence w, r"%} + + ins_encode %{ + __ block_comment("membar_storeload_rvwmo"); + __ membar(MacroAssembler::StoreLoad); + %} + + ins_pipe(pipe_serial); +%} + instruct membar_volatile_rvwmo() %{ predicate(!UseZtso); match(MemBarVolatile); @@ -8279,6 +8323,22 @@ instruct unnecessary_membar_volatile_rvwmo() %{ ins_pipe(real_empty); %} +instruct membar_full_rvwmo() %{ + predicate(!UseZtso); + match(MemBarFull); + ins_cost(VOLATILE_REF_COST); + + format %{ "#@membar_full_rvwmo\n\t" + "fence rw, rw" %} + + ins_encode %{ + __ block_comment("membar_full_rvwmo"); + __ membar(MacroAssembler::AnyAny); + %} + + ins_pipe(pipe_serial); +%} + instruct spin_wait() %{ predicate(UseZihintpause); match(OnSpinWait); diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp index 03c843efc69..11a88dfedd7 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp @@ -55,7 +55,7 @@ class VM_Version : public Abstract_VM_Version { public: RVFeatureValue(const char* pretty, int linux_bit_num, bool fstring) : - _pretty(pretty), _feature_string(fstring), _linux_feature_bit(nth_bit(linux_bit_num)) { + _pretty(pretty), _feature_string(fstring), _linux_feature_bit(nth_bit(linux_bit_num)) { } virtual void enable_feature(int64_t value = 0) = 0; virtual void disable_feature() = 0; diff --git a/src/hotspot/cpu/s390/c1_globals_s390.hpp b/src/hotspot/cpu/s390/c1_globals_s390.hpp index bd07dd87066..64cc239800a 100644 --- a/src/hotspot/cpu/s390/c1_globals_s390.hpp +++ b/src/hotspot/cpu/s390/c1_globals_s390.hpp @@ -51,7 +51,6 @@ define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M); define_pd_global(size_t, CodeCacheExpansionSize, 32*K); define_pd_global(size_t, CodeCacheMinBlockLength, 1); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); -define_pd_global(bool, NeverActAsServerClassMachine, true); define_pd_global(size_t, InitialCodeCacheSize, 160*K); #endif // !COMPILER2 diff --git a/src/hotspot/cpu/s390/c2_globals_s390.hpp b/src/hotspot/cpu/s390/c2_globals_s390.hpp index 068511be8f3..eee3a8588c3 100644 --- a/src/hotspot/cpu/s390/c2_globals_s390.hpp +++ b/src/hotspot/cpu/s390/c2_globals_s390.hpp @@ -78,7 +78,4 @@ define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed on z/Architecture. -// Ergonomics related flags -define_pd_global(bool, NeverActAsServerClassMachine, false); - #endif // CPU_S390_C2_GLOBALS_S390_HPP diff --git a/src/hotspot/cpu/s390/downcallLinker_s390.cpp b/src/hotspot/cpu/s390/downcallLinker_s390.cpp index ccd8002da37..f1c41d05b5c 100644 --- a/src/hotspot/cpu/s390/downcallLinker_s390.cpp +++ b/src/hotspot/cpu/s390/downcallLinker_s390.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -129,7 +129,7 @@ void DowncallLinker::StubGenerator::generate() { assert(!_needs_return_buffer, "unexpected needs_return_buffer"); RegSpiller out_reg_spiller(_output_registers); - int spill_offset = allocated_frame_size; + int out_spill_offset = allocated_frame_size; allocated_frame_size += BytesPerWord; StubLocations locs; @@ -153,6 +153,18 @@ void DowncallLinker::StubGenerator::generate() { GrowableArray out_regs = ForeignGlobals::replace_place_holders(_input_registers, locs); ArgumentShuffle arg_shuffle(filtered_java_regs, out_regs, _abi._scratch1); + // Need to spill for state capturing runtime call. + // The area spilled into is distinct from the capture state buffer. + RegSpiller in_reg_spiller(out_regs); + int in_spill_offset = -1; + if (_captured_state_mask != 0) { + // The spill area cannot be shared with the out_spill since + // spilling needs to happen before the call. Allocate a new + // region in the stack for this spill space. + in_spill_offset = allocated_frame_size; + allocated_frame_size += in_reg_spiller.spill_size_bytes(); + } + #ifndef PRODUCT LogTarget(Trace, foreign, downcall) lt; if (lt.is_enabled()) { @@ -192,6 +204,21 @@ void DowncallLinker::StubGenerator::generate() { arg_shuffle.generate(_masm, shuffle_reg, frame::z_jit_out_preserve_size, _abi._shadow_space_bytes); __ block_comment("} argument_shuffle"); + if (_captured_state_mask != 0) { + assert(in_spill_offset != -1, "must be"); + __ block_comment("{ load initial thread local"); + in_reg_spiller.generate_spill(_masm, in_spill_offset); + + // Copy the contents of the capture state buffer into thread local + __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, DowncallLinker::capture_state_pre)); + __ z_lg(Z_ARG1, Address(Z_SP, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER))); + __ load_const_optimized(Z_ARG2, _captured_state_mask); + __ call(call_target_address); + + in_reg_spiller.generate_fill(_masm, in_spill_offset); + __ block_comment("} load initial thread local"); + } + __ call(as_Register(locs.get(StubLocations::TARGET_ADDRESS))); ////////////////////////////////////////////////////////////////////////////// @@ -199,14 +226,14 @@ void DowncallLinker::StubGenerator::generate() { if (_captured_state_mask != 0) { __ block_comment("save_thread_local {"); - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); - __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, DowncallLinker::capture_state)); + __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, DowncallLinker::capture_state_post)); __ z_lg(Z_ARG1, Address(Z_SP, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER))); __ load_const_optimized(Z_ARG2, _captured_state_mask); __ call(call_target_address); - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); __ block_comment("} save_thread_local"); } @@ -259,13 +286,13 @@ void DowncallLinker::StubGenerator::generate() { __ bind(L_safepoint_poll_slow_path); // Need to save the native result registers around any runtime calls. - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, JavaThread::check_special_condition_for_native_trans)); __ z_lgr(Z_ARG1, Z_thread); __ call(call_target_address); - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); __ z_bru(L_after_safepoint_poll); __ block_comment("} L_safepoint_poll_slow_path"); @@ -275,12 +302,12 @@ void DowncallLinker::StubGenerator::generate() { __ bind(L_reguard); // Need to save the native result registers around any runtime calls. - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, SharedRuntime::reguard_yellow_pages)); __ call(call_target_address); - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); __ z_bru(L_after_reguard); diff --git a/src/hotspot/cpu/s390/frame_s390.hpp b/src/hotspot/cpu/s390/frame_s390.hpp index ad754706367..bcdeec43e1a 100644 --- a/src/hotspot/cpu/s390/frame_s390.hpp +++ b/src/hotspot/cpu/s390/frame_s390.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -463,7 +463,7 @@ // Accessors - inline intptr_t* fp() const { return _fp; } + inline intptr_t* fp() const { assert_absolute(); return _fp; } private: diff --git a/src/hotspot/cpu/s390/frame_s390.inline.hpp b/src/hotspot/cpu/s390/frame_s390.inline.hpp index dea0e72581f..6fcd36c57d1 100644 --- a/src/hotspot/cpu/s390/frame_s390.inline.hpp +++ b/src/hotspot/cpu/s390/frame_s390.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -133,10 +133,10 @@ inline void frame::interpreter_frame_set_monitors(BasicObjectLock* monitors) { // Return unique id for this frame. The id must have a value where we // can distinguish identity and younger/older relationship. null -// represents an invalid (incomparable) frame. +// represents an invalid (incomparable) frame. Should not be called for heap frames. inline intptr_t* frame::id(void) const { // Use _fp. _sp or _unextended_sp wouldn't be correct due to resizing. - return _fp; + return real_fp(); } // Return true if this frame is older (less recent activation) than diff --git a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp index 7617c7a49e8..9fac231df47 100644 --- a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp @@ -169,6 +169,11 @@ void BarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, Re __ z_lg(obj, 0, obj); // Resolve (untagged) jobject. } +void BarrierSetAssembler::try_resolve_weak_handle(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) { + // Load the oop from the weak handle. + __ z_lg(obj, Address(obj)); +} + void BarrierSetAssembler::nmethod_entry_barrier(MacroAssembler* masm) { BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod(); __ align(4, __ offset() + OFFSET_TO_PATCHABLE_DATA); // must align the following block which requires atomic updates @@ -206,11 +211,6 @@ OptoReg::Name BarrierSetAssembler::refine_register(const Node* node, OptoReg::Na return opto_reg; } -void BarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) { - // Load the oop from the weak handle. - __ z_lg(obj, Address(obj)); -} - #undef __ #define __ _masm-> diff --git a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp index d5682450414..8e76ec2f4b4 100644 --- a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp @@ -58,6 +58,11 @@ public: virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, Register obj, Register tmp, Label& slowpath); + // Can be used in nmethods including native wrappers. + // Attention: obj will only be valid until next safepoint (no SATB barrier). + // (other platforms currently use it for C2 only: try_resolve_weak_handle_in_c2) + virtual void try_resolve_weak_handle(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path); + virtual void nmethod_entry_barrier(MacroAssembler* masm); virtual void barrier_stubs_init() {} @@ -65,8 +70,6 @@ public: #ifdef COMPILER2 OptoReg::Name refine_register(const Node* node, OptoReg::Name opto_reg) const; - virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, - Register tmp, Label& slow_path); #endif // COMPILER2 static const int OFFSET_TO_PATCHABLE_DATA_INSTRUCTION = 6 + 6 + 6; // iihf(6) + iilf(6) + lg(6) diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index 78779a9098a..6e132f895bd 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. - * Copyright 2024 IBM Corporation. All rights reserved. + * Copyright 2024, 2026 IBM Corporation. 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 @@ -6413,7 +6413,7 @@ void MacroAssembler::compiler_fast_lock_object(Register obj, Register box, Regis // Check if object matches. z_lg(tmp2, Address(tmp1_monitor, ObjectMonitor::object_offset())); BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); - bs_asm->try_resolve_weak_handle_in_c2(this, tmp2, Z_R0_scratch, slow_path); + bs_asm->try_resolve_weak_handle(this, tmp2, Z_R0_scratch, slow_path); z_cgr(obj, tmp2); z_brne(slow_path); diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index 1521edde40c..2208a197ac9 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -1929,10 +1929,6 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? 15 : FLOATPRESSURE; } -bool Matcher::use_asm_for_ldiv_by_con(jlong divisor) { - return false; -} - // Register for DIVI projection of divmodI const RegMask& Matcher::divI_proj_mask() { return _Z_RARG4_INT_REG_mask; @@ -5239,6 +5235,15 @@ instruct membar_release_lock() %{ ins_pipe(pipe_class_dummy); %} +instruct membar_storeload() %{ + match(MemBarStoreLoad); + ins_cost(4 * MEMORY_REF_COST); + size(2); + format %{ "MEMBAR-storeload" %} + ins_encode %{ __ z_fence(); %} + ins_pipe(pipe_class_dummy); +%} + instruct membar_volatile() %{ match(MemBarVolatile); ins_cost(4 * MEMORY_REF_COST); @@ -5258,6 +5263,15 @@ instruct unnecessary_membar_volatile() %{ ins_pipe(pipe_class_dummy); %} +instruct membar_full() %{ + match(MemBarFull); + ins_cost(4 * MEMORY_REF_COST); + size(2); + format %{ "MEMBAR-full" %} + ins_encode %{ __ z_fence(); %} + ins_pipe(pipe_class_dummy); +%} + instruct membar_CPUOrder() %{ match(MemBarCPUOrder); ins_cost(0); diff --git a/src/hotspot/cpu/x86/c1_globals_x86.hpp b/src/hotspot/cpu/x86/c1_globals_x86.hpp index 063a9185d53..bb75a31a77c 100644 --- a/src/hotspot/cpu/x86/c1_globals_x86.hpp +++ b/src/hotspot/cpu/x86/c1_globals_x86.hpp @@ -50,7 +50,6 @@ define_pd_global(bool, ProfileInterpreter, false); define_pd_global(size_t, CodeCacheExpansionSize, 32*K ); define_pd_global(size_t, CodeCacheMinBlockLength, 1 ); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); -define_pd_global(bool, NeverActAsServerClassMachine, true ); define_pd_global(bool, CICompileOSR, true ); #endif // !COMPILER2 define_pd_global(bool, UseTypeProfile, false); diff --git a/src/hotspot/cpu/x86/c2_globals_x86.hpp b/src/hotspot/cpu/x86/c2_globals_x86.hpp index bc119693d32..11d8c03d0ca 100644 --- a/src/hotspot/cpu/x86/c2_globals_x86.hpp +++ b/src/hotspot/cpu/x86/c2_globals_x86.hpp @@ -73,7 +73,4 @@ define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed on x86. -// Ergonomics related flags -define_pd_global(bool, NeverActAsServerClassMachine, false); - #endif // CPU_X86_C2_GLOBALS_X86_HPP diff --git a/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp b/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp index c48940198ea..e3bf5f17fe9 100644 --- a/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp +++ b/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -145,10 +145,10 @@ void DowncallLinker::StubGenerator::generate() { // when we don't use a return buffer we need to spill the return value around our slow path calls bool should_save_return_value = !_needs_return_buffer; RegSpiller out_reg_spiller(_output_registers); - int spill_rsp_offset = -1; + int out_spill_rsp_offset = -1; if (should_save_return_value) { - spill_rsp_offset = 0; + out_spill_rsp_offset = 0; // spill area can be shared with shadow space and out args, // since they are only used before the call, // and spill area is only used after. @@ -173,6 +173,9 @@ void DowncallLinker::StubGenerator::generate() { // FP-> | | // |---------------------| = frame_bottom_offset = frame_size // | (optional) | + // | in_reg_spiller area | + // |---------------------| + // | (optional) | // | capture state buf | // |---------------------| = StubLocations::CAPTURED_STATE_BUFFER // | (optional) | @@ -188,6 +191,18 @@ void DowncallLinker::StubGenerator::generate() { VMStorage shuffle_reg = as_VMStorage(rbx); ArgumentShuffle arg_shuffle(filtered_java_regs, out_regs, shuffle_reg); + // Need to spill for state capturing runtime call. + // The area spilled into is distinct from the capture state buffer. + RegSpiller in_reg_spiller(out_regs); + int in_spill_rsp_offset = -1; + if (_captured_state_mask != 0) { + // The spill area cannot be shared with the shadow/out args space + // since spilling needs to happen before the call. Allocate a new + // region in the stack for this spill space. + in_spill_rsp_offset = allocated_frame_size; + allocated_frame_size += in_reg_spiller.spill_size_bytes(); + } + #ifndef PRODUCT LogTarget(Trace, foreign, downcall) lt; if (lt.is_enabled()) { @@ -232,6 +247,19 @@ void DowncallLinker::StubGenerator::generate() { arg_shuffle.generate(_masm, shuffle_reg, 0, _abi._shadow_space_bytes); __ block_comment("} argument shuffle"); + if (_captured_state_mask != 0) { + assert(in_spill_rsp_offset != -1, "must be"); + __ block_comment("{ load initial thread local"); + in_reg_spiller.generate_spill(_masm, in_spill_rsp_offset); + + // Copy the contents of the capture state buffer into thread local + __ movptr(c_rarg0, Address(rsp, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER))); + __ movl(c_rarg1, _captured_state_mask); + runtime_call(_masm, CAST_FROM_FN_PTR(address, DowncallLinker::capture_state_pre)); + + in_reg_spiller.generate_fill(_masm, in_spill_rsp_offset); + __ block_comment("} load initial thread local"); + } __ call(as_Register(locs.get(StubLocations::TARGET_ADDRESS))); assert(!_abi.is_volatile_reg(r15_thread), "Call assumed not to kill r15"); @@ -258,15 +286,15 @@ void DowncallLinker::StubGenerator::generate() { __ block_comment("{ save thread local"); if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_rsp_offset); + out_reg_spiller.generate_spill(_masm, out_spill_rsp_offset); } __ movptr(c_rarg0, Address(rsp, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER))); __ movl(c_rarg1, _captured_state_mask); - runtime_call(_masm, CAST_FROM_FN_PTR(address, DowncallLinker::capture_state)); + runtime_call(_masm, CAST_FROM_FN_PTR(address, DowncallLinker::capture_state_post)); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_rsp_offset); + out_reg_spiller.generate_fill(_masm, out_spill_rsp_offset); } __ block_comment("} save thread local"); @@ -319,14 +347,14 @@ void DowncallLinker::StubGenerator::generate() { __ bind(L_safepoint_poll_slow_path); if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_rsp_offset); + out_reg_spiller.generate_spill(_masm, out_spill_rsp_offset); } __ mov(c_rarg0, r15_thread); runtime_call(_masm, CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans)); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_rsp_offset); + out_reg_spiller.generate_fill(_masm, out_spill_rsp_offset); } __ jmp(L_after_safepoint_poll); @@ -338,13 +366,13 @@ void DowncallLinker::StubGenerator::generate() { __ bind(L_reguard); if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_rsp_offset); + out_reg_spiller.generate_spill(_masm, out_spill_rsp_offset); } runtime_call(_masm, CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages)); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_rsp_offset); + out_reg_spiller.generate_fill(_masm, out_spill_rsp_offset); } __ jmp(L_after_reguard); diff --git a/src/hotspot/cpu/x86/frame_x86.inline.hpp b/src/hotspot/cpu/x86/frame_x86.inline.hpp index dcd766545d3..3f3b951edc8 100644 --- a/src/hotspot/cpu/x86/frame_x86.inline.hpp +++ b/src/hotspot/cpu/x86/frame_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -231,8 +231,8 @@ inline bool frame::equal(frame other) const { // Return unique id for this frame. The id must have a value where we can distinguish // identity and younger/older relationship. null represents an invalid (incomparable) -// frame. -inline intptr_t* frame::id(void) const { return unextended_sp(); } +// frame. Should not be called for heap frames. +inline intptr_t* frame::id(void) const { return real_fp(); } // Return true if the frame is older (less recent activation) than the frame represented by id inline bool frame::is_older(intptr_t* id) const { assert(this->id() != nullptr && id != nullptr, "null frame id"); @@ -397,6 +397,9 @@ inline frame frame::sender(RegisterMap* map) const { StackWatermarkSet::on_iteration(map->thread(), result); } + // Calling frame::id() is currently not supported for heap frames. + assert(result._on_heap || this->_on_heap || result.is_older(this->id()), "Must be"); + return result; } diff --git a/src/hotspot/cpu/x86/globals_x86.hpp b/src/hotspot/cpu/x86/globals_x86.hpp index 4f5b6d31e75..6de46752790 100644 --- a/src/hotspot/cpu/x86/globals_x86.hpp +++ b/src/hotspot/cpu/x86/globals_x86.hpp @@ -117,9 +117,6 @@ define_pd_global(intx, InitArrayShortSize, 8*BytesPerLong); product(bool, UseIncDec, true, DIAGNOSTIC, \ "Use INC, DEC instructions on x86") \ \ - product(bool, UseNewLongLShift, false, \ - "Use optimized bitwise shift left") \ - \ product(bool, UseAddressNop, false, \ "Use '0F 1F [addr]' NOP instructions on x86 cpus") \ \ @@ -168,16 +165,27 @@ define_pd_global(intx, InitArrayShortSize, 8*BytesPerLong); "Perform Ecore Optimization") \ \ /* Minimum array size in bytes to use AVX512 intrinsics */ \ - /* for copy, inflate and fill which don't bail out early based on any */ \ + /* for inflate and fill which don't bail out early based on any */ \ /* condition. When this value is set to zero compare operations like */ \ /* compare, vectorizedMismatch, compress can also use AVX512 intrinsics.*/\ product(int, AVX3Threshold, 4096, DIAGNOSTIC, \ "Minimum array size in bytes to use AVX512 intrinsics" \ - "for copy, inflate and fill. When this value is set as zero" \ + "for inflate and fill. When this value is set as zero" \ "compare operations can also use AVX512 intrinsics.") \ range(0, max_jint) \ constraint(AVX3ThresholdConstraintFunc,AfterErgo) \ \ + /* Minimum array size in bytes to use AVX512 intrinsics */ \ + /* for copy and fill which don't bail out early based on any */ \ + /* condition. When this value is set to zero clear operations that */ \ + /* work on memory blocks can also use AVX512 intrinsics. */ \ + product(int, CopyAVX3Threshold, 4096, DIAGNOSTIC, \ + "Minimum array size in bytes to use AVX512 intrinsics" \ + "for copy and fill. When this value is set as zero" \ + "clear operations can also use AVX512 intrinsics.") \ + range(0, max_jint) \ + constraint(CopyAVX3ThresholdConstraintFunc,AfterErgo) \ + \ product(bool, IntelJccErratumMitigation, true, DIAGNOSTIC, \ "Turn off JVM mitigations related to Intel micro code " \ "mitigations for the Intel JCC erratum") \ diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 2d46a50d426..1d77be26bd9 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -5820,7 +5820,7 @@ void MacroAssembler::xmm_clear_mem(Register base, Register cnt, Register rtmp, X // cnt - number of qwords (8-byte words). // base - start address, qword aligned. Label L_zero_64_bytes, L_loop, L_sloop, L_tail, L_end; - bool use64byteVector = (MaxVectorSize == 64) && (VM_Version::avx3_threshold() == 0); + bool use64byteVector = (MaxVectorSize == 64) && (CopyAVX3Threshold == 0); if (use64byteVector) { vpxor(xtmp, xtmp, xtmp, AVX_512bit); } else if (MaxVectorSize >= 32) { @@ -5884,7 +5884,7 @@ void MacroAssembler::xmm_clear_mem(Register base, Register cnt, Register rtmp, X // Clearing constant sized memory using YMM/ZMM registers. void MacroAssembler::clear_mem(Register base, int cnt, Register rtmp, XMMRegister xtmp, KRegister mask) { assert(UseAVX > 2 && VM_Version::supports_avx512vl(), ""); - bool use64byteVector = (MaxVectorSize > 32) && (VM_Version::avx3_threshold() == 0); + bool use64byteVector = (MaxVectorSize > 32) && (CopyAVX3Threshold == 0); int vector64_count = (cnt & (~0x7)) >> 3; cnt = cnt & 0x7; @@ -6109,8 +6109,8 @@ void MacroAssembler::generate_fill(BasicType t, bool aligned, // Fill 64-byte chunks Label L_fill_64_bytes_loop_avx3, L_check_fill_64_bytes_avx2; - // If number of bytes to fill < VM_Version::avx3_threshold(), perform fill using AVX2 - cmpptr(count, VM_Version::avx3_threshold()); + // If number of bytes to fill < CopyAVX3Threshold, perform fill using AVX2 + cmpptr(count, CopyAVX3Threshold); jccb(Assembler::below, L_check_fill_64_bytes_avx2); vpbroadcastd(xtmp, xtmp, Assembler::AVX_512bit); @@ -9483,7 +9483,6 @@ void MacroAssembler::generate_fill_avx3(BasicType type, Register to, Register va Label L_fill_zmm_sequence; int shift = -1; - int avx3threshold = VM_Version::avx3_threshold(); switch(type) { case T_BYTE: shift = 0; break; @@ -9499,10 +9498,10 @@ void MacroAssembler::generate_fill_avx3(BasicType type, Register to, Register va fatal("Unhandled type: %s\n", type2name(type)); } - if ((avx3threshold != 0) || (MaxVectorSize == 32)) { + if ((CopyAVX3Threshold != 0) || (MaxVectorSize == 32)) { if (MaxVectorSize == 64) { - cmpq(count, avx3threshold >> shift); + cmpq(count, CopyAVX3Threshold >> shift); jcc(Assembler::greater, L_fill_zmm_sequence); } diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp index 36315535d16..332add6dcd4 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -166,12 +166,12 @@ class StubGenerator: public StubCodeGenerator { // - If target supports AVX3 features (BW+VL+F) then implementation uses 32 byte vectors (YMMs) // for both special cases (various small block sizes) and aligned copy loop. This is the // default configuration. - // - If copy length is above AVX3Threshold, then implementation use 64 byte vectors (ZMMs) + // - If copy length is above CopyAVX3Threshold, then implementation use 64 byte vectors (ZMMs) // for main copy loop (and subsequent tail) since bulk of the cycles will be consumed in it. // - If user forces MaxVectorSize=32 then above 4096 bytes its seen that REP MOVs shows a // better performance for disjoint copies. For conjoint/backward copy vector based // copy performs better. - // - If user sets AVX3Threshold=0, then special cases for small blocks sizes operate over + // - If user sets CopyAVX3Threshold=0, then special cases for small blocks sizes operate over // 64 byte vector registers (ZMMs). address generate_disjoint_copy_avx3_masked(StubId stub_id, address* entry); @@ -330,6 +330,19 @@ class StubGenerator: public StubCodeGenerator { void aesecb_decrypt(Register source_addr, Register dest_addr, Register key, Register len); + // Shared implementation for ECB/AES Encrypt and Decrypt, which does 4 blocks + // in a loop at a time to hide instruction latency. Set is_encrypt=true for + // encryption, false for decryption. + address generate_electronicCodeBook_AESCrypt_Parallel(bool is_encrypt); + + // A version of ECB/AES Encrypt which does 4 blocks in a loop at a time + // to hide instruction latency + address generate_electronicCodeBook_encryptAESCrypt_Parallel(); + + // A version of ECB/AES Decrypt which does 4 blocks in a loop at a time + // to hide instruction latency + address generate_electronicCodeBook_decryptAESCrypt_Parallel(); + // Vector AES Galois Counter Mode implementation address generate_galoisCounterMode_AESCrypt(); void aesgcm_encrypt(Register in, Register len, Register ct, Register out, Register key, diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_adler.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_adler.cpp index 2799997a761..1d3e7afde1d 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_adler.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_adler.cpp @@ -144,7 +144,7 @@ address StubGenerator::generate_updateBytesAdler32() { __ align32(); if (VM_Version::supports_avx512vl()) { // AVX2 performs better for smaller inputs because of leaner post loop reduction sequence.. - __ cmpl(s, MAX2(128, VM_Version::avx3_threshold())); + __ cmpl(s, MAX2(128, CopyAVX3Threshold)); __ jcc(Assembler::belowEqual, SLOOP1A_AVX2); __ lea(end, Address(s, data, Address::times_1, - (2*CHUNKSIZE -1))); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp index 24de32a6fe7..1fa80c9d967 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2019, 2025, Intel Corporation. All rights reserved. +* Copyright (c) 2019, 2026, Intel Corporation. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -218,6 +218,8 @@ void StubGenerator::generate_aes_stubs() { StubRoutines::_galoisCounterMode_AESCrypt = generate_galoisCounterMode_AESCrypt(); } else { StubRoutines::_cipherBlockChaining_decryptAESCrypt = generate_cipherBlockChaining_decryptAESCrypt_Parallel(); + StubRoutines::_electronicCodeBook_encryptAESCrypt = generate_electronicCodeBook_encryptAESCrypt_Parallel(); + StubRoutines::_electronicCodeBook_decryptAESCrypt = generate_electronicCodeBook_decryptAESCrypt_Parallel(); if (VM_Version::supports_avx2()) { StubRoutines::_galoisCounterMode_AESCrypt = generate_avx2_galoisCounterMode_AESCrypt(); } @@ -1399,6 +1401,200 @@ address StubGenerator::generate_cipherBlockChaining_encryptAESCrypt() { return start; } +// This is a version of ECB/AES Encrypt/Decrypt which does 4 blocks in a loop +// at a time to hide instruction latency. +// +// For encryption (is_encrypt=true): +// pxor key[0], aesenc key[1..rounds-1], aesenclast key[rounds] +// For decryption (is_encrypt=false): +// pxor key[1], aesdec key[2..rounds], aesdeclast key[0] +// +// Arguments: +// +// Inputs: +// c_rarg0 - source byte array address +// c_rarg1 - destination byte array address +// c_rarg2 - session key (Ke/Kd) in little endian int array +// c_rarg3 - input length (must be multiple of blocksize 16) +// +// Output: +// rax - input length +// +address StubGenerator::generate_electronicCodeBook_AESCrypt_Parallel(bool is_encrypt) { + assert(UseAES, "need AES instructions and misaligned SSE support"); + __ align(CodeEntryAlignment); + StubId stub_id = is_encrypt ? StubId::stubgen_electronicCodeBook_encryptAESCrypt_id + : StubId::stubgen_electronicCodeBook_decryptAESCrypt_id; + StubCodeMark mark(this, stub_id); + address start = __ pc(); + + const Register from = c_rarg0; // source array address + const Register to = c_rarg1; // destination array address + const Register key = c_rarg2; // key array address + const Register len_reg = c_rarg3; // src len (must be multiple of blocksize 16) + const Register pos = rax; + const Register keylen = r11; + + const XMMRegister xmm_result0 = xmm0; + const XMMRegister xmm_result1 = xmm1; + const XMMRegister xmm_result2 = xmm2; + const XMMRegister xmm_result3 = xmm3; + const XMMRegister xmm_key_shuf_mask = xmm4; + const XMMRegister xmm_key_tmp = xmm5; + // keys 0-9 pre-loaded into xmm6-xmm15 + const int XMM_REG_NUM_KEY_FIRST = 6; + const int XMM_REG_NUM_KEY_LAST = 15; + const XMMRegister xmm_key_first = as_XMMRegister(XMM_REG_NUM_KEY_FIRST); + + // for key_128, key_192, key_256 + const int ROUNDS[3] = {10, 12, 14}; + + Label L_exit; + Label L_loop4[3], L_single[3], L_done[3]; + +#ifdef DoFour +#undef DoFour +#endif +#ifdef DoOne +#undef DoOne +#endif + +#define DoFour(opc, reg) \ +__ opc(xmm_result0, reg); \ +__ opc(xmm_result1, reg); \ +__ opc(xmm_result2, reg); \ +__ opc(xmm_result3, reg); + +#define DoOne(opc, reg) \ +__ opc(xmm_result0, reg); + + __ enter(); // required for proper stackwalking of RuntimeStub frame + __ push(len_reg); // save original length for return value + + __ movl(keylen, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); + + __ movdqu(xmm_key_shuf_mask, ExternalAddress(key_shuffle_mask_addr()), r10 /*rscratch*/); + // load up xmm regs 6 thru 15 with keys 0x00 - 0x90 + for (int rnum = XMM_REG_NUM_KEY_FIRST, offset = 0x00; rnum <= XMM_REG_NUM_KEY_LAST; rnum++, offset += 0x10) { + load_key(as_XMMRegister(rnum), key, offset, xmm_key_shuf_mask); + } + __ xorptr(pos, pos); + + // key length could be only {11, 13, 15} * 4 = {44, 52, 60} + __ cmpl(keylen, 52); + __ jcc(Assembler::equal, L_loop4[1]); + __ cmpl(keylen, 60); + __ jcc(Assembler::equal, L_loop4[2]); + + // k == 0: generate code for key_128 + // k == 1: generate code for key_192 + // k == 2: generate code for key_256 + for (int k = 0; k < 3; ++k) { + __ align(OptoLoopAlignment); + __ BIND(L_loop4[k]); + __ cmpptr(len_reg, 4 * AESBlockSize); + __ jcc(Assembler::less, L_single[k]); + + __ movdqu(xmm_result0, Address(from, pos, Address::times_1, 0 * AESBlockSize)); + __ movdqu(xmm_result1, Address(from, pos, Address::times_1, 1 * AESBlockSize)); + __ movdqu(xmm_result2, Address(from, pos, Address::times_1, 2 * AESBlockSize)); + __ movdqu(xmm_result3, Address(from, pos, Address::times_1, 3 * AESBlockSize)); + + if (is_encrypt) { + DoFour(pxor, xmm_key_first); + for (int rnum = 1; rnum < 10; rnum++) { + DoFour(aesenc, as_XMMRegister(rnum + XMM_REG_NUM_KEY_FIRST)); + } + for (int i = 10; i < ROUNDS[k]; i++) { + load_key(xmm_key_tmp, key, i * 0x10, xmm_key_shuf_mask); + DoFour(aesenc, xmm_key_tmp); + } + load_key(xmm_key_tmp, key, ROUNDS[k] * 0x10, xmm_key_shuf_mask); + DoFour(aesenclast, xmm_key_tmp); + } else { + DoFour(pxor, as_XMMRegister(1 + XMM_REG_NUM_KEY_FIRST)); + for (int rnum = 2; rnum < 10; rnum++) { + DoFour(aesdec, as_XMMRegister(rnum + XMM_REG_NUM_KEY_FIRST)); + } + for (int i = 10; i <= ROUNDS[k]; i++) { + load_key(xmm_key_tmp, key, i * 0x10, xmm_key_shuf_mask); + DoFour(aesdec, xmm_key_tmp); + } + DoFour(aesdeclast, xmm_key_first); + } + + __ movdqu(Address(to, pos, Address::times_1, 0 * AESBlockSize), xmm_result0); + __ movdqu(Address(to, pos, Address::times_1, 1 * AESBlockSize), xmm_result1); + __ movdqu(Address(to, pos, Address::times_1, 2 * AESBlockSize), xmm_result2); + __ movdqu(Address(to, pos, Address::times_1, 3 * AESBlockSize), xmm_result3); + + __ addptr(pos, 4 * AESBlockSize); + __ subptr(len_reg, 4 * AESBlockSize); + __ jmp(L_loop4[k]); + + __ align(OptoLoopAlignment); + __ BIND(L_single[k]); + __ cmpptr(len_reg, AESBlockSize); + __ jcc(Assembler::less, L_done[k]); + + __ movdqu(xmm_result0, Address(from, pos, Address::times_1, 0)); + + if (is_encrypt) { + DoOne(pxor, xmm_key_first); + for (int rnum = 1; rnum < 10; rnum++) { + DoOne(aesenc, as_XMMRegister(rnum + XMM_REG_NUM_KEY_FIRST)); + } + for (int i = 10; i < ROUNDS[k]; i++) { + load_key(xmm_key_tmp, key, i * 0x10, xmm_key_shuf_mask); + DoOne(aesenc, xmm_key_tmp); + } + load_key(xmm_key_tmp, key, ROUNDS[k] * 0x10, xmm_key_shuf_mask); + DoOne(aesenclast, xmm_key_tmp); + } else { + DoOne(pxor, as_XMMRegister(1 + XMM_REG_NUM_KEY_FIRST)); + for (int rnum = 2; rnum < 10; rnum++) { + DoOne(aesdec, as_XMMRegister(rnum + XMM_REG_NUM_KEY_FIRST)); + } + for (int i = 10; i <= ROUNDS[k]; i++) { + load_key(xmm_key_tmp, key, i * 0x10, xmm_key_shuf_mask); + DoOne(aesdec, xmm_key_tmp); + } + DoOne(aesdeclast, xmm_key_first); + } + + __ movdqu(Address(to, pos, Address::times_1, 0), xmm_result0); + __ addptr(pos, AESBlockSize); + __ subptr(len_reg, AESBlockSize); + __ jmp(L_single[k]); + + __ BIND(L_done[k]); + if (k < 2) __ jmp(L_exit); + } //for key_128/192/256 + + __ BIND(L_exit); + // Clear all XMM registers holding sensitive key material before returning + __ pxor(xmm_key_tmp, xmm_key_tmp); + for (int rnum = XMM_REG_NUM_KEY_FIRST; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) { + __ pxor(as_XMMRegister(rnum), as_XMMRegister(rnum)); + } + __ pop(rax); + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; + +#undef DoFour +#undef DoOne +} + +address StubGenerator::generate_electronicCodeBook_encryptAESCrypt_Parallel() { + return generate_electronicCodeBook_AESCrypt_Parallel(true); +} + +address StubGenerator::generate_electronicCodeBook_decryptAESCrypt_Parallel() { + return generate_electronicCodeBook_AESCrypt_Parallel(false); +} + // This is a version of CBC/AES Decrypt which does 4 blocks in a loop at a time // to hide instruction latency // @@ -1493,7 +1689,7 @@ address StubGenerator::generate_cipherBlockChaining_decryptAESCrypt_Parallel() { __ opc(xmm_result0, src_reg); \ __ opc(xmm_result1, src_reg); \ __ opc(xmm_result2, src_reg); \ -__ opc(xmm_result3, src_reg); \ +__ opc(xmm_result3, src_reg); for (int k = 0; k < 3; ++k) { __ BIND(L_multiBlock_loopTopHead[k]); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp index d53fafafdb4..01e004b7b43 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp @@ -511,12 +511,12 @@ void StubGenerator::copy_bytes_backward(Register from, Register dest, // - If target supports AVX3 features (BW+VL+F) then implementation uses 32 byte vectors (YMMs) // for both special cases (various small block sizes) and aligned copy loop. This is the // default configuration. -// - If copy length is above AVX3Threshold, then implementation use 64 byte vectors (ZMMs) +// - If copy length is above CopyAVX3Threshold, then implementation use 64 byte vectors (ZMMs) // for main copy loop (and subsequent tail) since bulk of the cycles will be consumed in it. // - If user forces MaxVectorSize=32 then above 4096 bytes its seen that REP MOVs shows a // better performance for disjoint copies. For conjoint/backward copy vector based // copy performs better. -// - If user sets AVX3Threshold=0, then special cases for small blocks sizes operate over +// - If user sets CopyAVX3Threshold=0, then special cases for small blocks sizes operate over // 64 byte vector registers (ZMMs). // Inputs: @@ -575,8 +575,7 @@ address StubGenerator::generate_disjoint_copy_avx3_masked(StubId stub_id, addres StubCodeMark mark(this, stub_id); address start = __ pc(); - int avx3threshold = VM_Version::avx3_threshold(); - bool use64byteVector = (MaxVectorSize > 32) && (avx3threshold == 0); + bool use64byteVector = (MaxVectorSize > 32) && (CopyAVX3Threshold == 0); const int large_threshold = 2621440; // 2.5 MB Label L_main_loop, L_main_loop_64bytes, L_tail, L_tail64, L_exit, L_entry; Label L_repmovs, L_main_pre_loop, L_main_pre_loop_64bytes, L_pre_main_post_64; @@ -647,7 +646,7 @@ address StubGenerator::generate_disjoint_copy_avx3_masked(StubId stub_id, addres __ cmpq(temp2, large_threshold); __ jcc(Assembler::greaterEqual, L_copy_large); } - if (avx3threshold != 0) { + if (CopyAVX3Threshold != 0) { __ cmpq(count, threshold[shift]); if (MaxVectorSize == 64) { // Copy using 64 byte vectors. @@ -659,7 +658,7 @@ address StubGenerator::generate_disjoint_copy_avx3_masked(StubId stub_id, addres } } - if ((MaxVectorSize < 64) || (avx3threshold != 0)) { + if ((MaxVectorSize < 64) || (CopyAVX3Threshold != 0)) { // Partial copy to make dst address 32 byte aligned. __ movq(temp2, to); __ andq(temp2, 31); @@ -913,8 +912,7 @@ address StubGenerator::generate_conjoint_copy_avx3_masked(StubId stub_id, addres StubCodeMark mark(this, stub_id); address start = __ pc(); - int avx3threshold = VM_Version::avx3_threshold(); - bool use64byteVector = (MaxVectorSize > 32) && (avx3threshold == 0); + bool use64byteVector = (MaxVectorSize > 32) && (CopyAVX3Threshold == 0); Label L_main_pre_loop, L_main_pre_loop_64bytes, L_pre_main_post_64; Label L_main_loop, L_main_loop_64bytes, L_tail, L_tail64, L_exit, L_entry; @@ -979,12 +977,12 @@ address StubGenerator::generate_conjoint_copy_avx3_masked(StubId stub_id, addres // PRE-MAIN-POST loop for aligned copy. __ BIND(L_entry); - if ((MaxVectorSize > 32) && (avx3threshold != 0)) { + if ((MaxVectorSize > 32) && (CopyAVX3Threshold != 0)) { __ cmpq(temp1, threshold[shift]); __ jcc(Assembler::greaterEqual, L_pre_main_post_64); } - if ((MaxVectorSize < 64) || (avx3threshold != 0)) { + if ((MaxVectorSize < 64) || (CopyAVX3Threshold != 0)) { // Partial copy to make dst address 32 byte aligned. __ leaq(temp2, Address(to, temp1, (Address::ScaleFactor)(shift), 0)); __ andq(temp2, 31); @@ -1199,7 +1197,7 @@ void StubGenerator::arraycopy_avx3_special_cases_conjoint(XMMRegister xmm, KRegi bool use64byteVector, Label& L_entry, Label& L_exit) { Label L_entry_64, L_entry_96, L_entry_128; Label L_entry_160, L_entry_192; - bool avx3 = (MaxVectorSize > 32) && (VM_Version::avx3_threshold() == 0); + bool avx3 = (MaxVectorSize > 32) && (CopyAVX3Threshold == 0); int size_mat[][6] = { /* T_BYTE */ {32 , 64, 96 , 128 , 160 , 192 }, diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index b352de77d6f..4301bd328d6 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -1508,9 +1508,6 @@ void VM_Version::get_processor_features() { MaxLoopPad = 11; } #endif // COMPILER2 - if (FLAG_IS_DEFAULT(UseXMMForArrayCopy)) { - UseXMMForArrayCopy = true; // use SSE2 movq on new ZX cpus - } if (supports_sse4_2()) { // new ZX cpus if (FLAG_IS_DEFAULT(UseUnalignedLoadStores)) { UseUnalignedLoadStores = true; // use movdqu on newest ZX cpus @@ -1528,10 +1525,6 @@ void VM_Version::get_processor_features() { // Use it on new AMD cpus starting from Opteron. UseAddressNop = true; } - if (supports_sse2() && FLAG_IS_DEFAULT(UseNewLongLShift)) { - // Use it on new AMD cpus starting from Opteron. - UseNewLongLShift = true; - } if (FLAG_IS_DEFAULT(UseXmmLoadAndClearUpper)) { if (supports_sse4a()) { UseXmmLoadAndClearUpper = true; // use movsd only on '10h' Opteron @@ -1571,10 +1564,6 @@ void VM_Version::get_processor_features() { if (FLAG_IS_DEFAULT(AllocatePrefetchInstr)) { FLAG_SET_DEFAULT(AllocatePrefetchInstr, 3); } - // On family 15h processors use XMM and UnalignedLoadStores for Array Copy - if (supports_sse2() && FLAG_IS_DEFAULT(UseXMMForArrayCopy)) { - FLAG_SET_DEFAULT(UseXMMForArrayCopy, true); - } if (supports_sse2() && FLAG_IS_DEFAULT(UseUnalignedLoadStores)) { FLAG_SET_DEFAULT(UseUnalignedLoadStores, true); } @@ -1591,9 +1580,6 @@ void VM_Version::get_processor_features() { if (cpu_family() >= 0x17) { // On family >=17h processors use XMM and UnalignedLoadStores // for Array Copy - if (supports_sse2() && FLAG_IS_DEFAULT(UseXMMForArrayCopy)) { - FLAG_SET_DEFAULT(UseXMMForArrayCopy, true); - } if (supports_sse2() && FLAG_IS_DEFAULT(UseUnalignedLoadStores)) { FLAG_SET_DEFAULT(UseUnalignedLoadStores, true); } @@ -1640,9 +1626,6 @@ void VM_Version::get_processor_features() { } #endif // COMPILER2 - if (FLAG_IS_DEFAULT(UseXMMForArrayCopy)) { - UseXMMForArrayCopy = true; // use SSE2 movq on new Intel cpus - } if ((supports_sse4_2() && supports_ht()) || supports_avx()) { // Newest Intel cpus if (FLAG_IS_DEFAULT(UseUnalignedLoadStores)) { UseUnalignedLoadStores = true; // use movdqu on newest Intel cpus @@ -1967,6 +1950,18 @@ void VM_Version::get_processor_features() { if (FLAG_IS_DEFAULT(UseCopySignIntrinsic)) { FLAG_SET_DEFAULT(UseCopySignIntrinsic, true); } + // CopyAVX3Threshold is the threshold at which 64-byte instructions are used + // for implementing the array copy and clear operations. + // The Intel platforms that supports the serialize instruction + // have improved implementation of 64-byte load/stores and so the default + // threshold is set to 0 for these platforms. + if (FLAG_IS_DEFAULT(CopyAVX3Threshold)) { + if (is_intel() && is_intel_server_family() && supports_serialize()) { + FLAG_SET_DEFAULT(CopyAVX3Threshold, 0); + } else { + FLAG_SET_DEFAULT(CopyAVX3Threshold, AVX3Threshold); + } + } } void VM_Version::print_platform_virtualization_info(outputStream* st) { @@ -2122,17 +2117,6 @@ bool VM_Version::is_intel_darkmont() { return is_intel() && is_intel_server_family() && (_model == 0xCC || _model == 0xDD); } -// avx3_threshold() sets the threshold at which 64-byte instructions are used -// for implementing the array copy and clear operations. -// The Intel platforms that supports the serialize instruction -// has improved implementation of 64-byte load/stores and so the default -// threshold is set to 0 for these platforms. -int VM_Version::avx3_threshold() { - return (is_intel_server_family() && - supports_serialize() && - FLAG_IS_DEFAULT(AVX3Threshold)) ? 0 : AVX3Threshold; -} - void VM_Version::clear_apx_test_state() { clear_apx_test_state_stub(); } diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp index e0a895737b7..a42558a8023 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp @@ -828,7 +828,7 @@ public: static uint32_t cpu_stepping() { return _cpuid_info.cpu_stepping(); } static int cpu_family() { return _cpu;} static bool is_P6() { return cpu_family() >= 6; } - static bool is_intel_server_family() { return cpu_family() == 6 || cpu_family() == 19; } + static bool is_intel_server_family() { return cpu_family() == 6 || cpu_family() == 18 || cpu_family() == 19; } static bool is_amd() { assert_is_initialized(); return _cpuid_info.std_vendor_name_0 == 0x68747541; } // 'htuA' static bool is_hygon() { assert_is_initialized(); return _cpuid_info.std_vendor_name_0 == 0x6F677948; } // 'ogyH' static bool is_amd_family() { return is_amd() || is_hygon(); } @@ -958,8 +958,6 @@ public: static bool is_intel_darkmont(); - static int avx3_threshold(); - static bool is_intel_tsc_synched_at_init(); static void insert_features_names(VM_Version::VM_Features features, stringStream& ss); diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index ed380105565..2fd4e5516fc 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -2763,13 +2763,6 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? default_float_pressure_threshold : FLOATPRESSURE; } -bool Matcher::use_asm_for_ldiv_by_con( jlong divisor ) { - // In 64 bit mode a code which use multiply when - // devisor is constant is faster than hardware - // DIV instruction (it uses MulHiL). - return false; -} - // Register for DIVI projection of divmodI const RegMask& Matcher::divI_proj_mask() { return INT_RAX_REG_mask(); @@ -8852,6 +8845,21 @@ instruct membar_release_lock() ins_pipe(empty); %} +instruct membar_storeload(rFlagsReg cr) %{ + match(MemBarStoreLoad); + effect(KILL cr); + ins_cost(400); + + format %{ + $$template + $$emit$$"lock addl [rsp + #0], 0\t! membar_storeload" + %} + ins_encode %{ + __ membar(Assembler::StoreLoad); + %} + ins_pipe(pipe_slow); +%} + instruct membar_volatile(rFlagsReg cr) %{ match(MemBarVolatile); effect(KILL cr); @@ -8879,6 +8887,21 @@ instruct unnecessary_membar_volatile() ins_pipe(empty); %} +instruct membar_full(rFlagsReg cr) %{ + match(MemBarFull); + effect(KILL cr); + ins_cost(400); + + format %{ + $$template + $$emit$$"lock addl [rsp + #0], 0\t! membar_full" + %} + ins_encode %{ + __ membar(Assembler::StoreLoad); + %} + ins_pipe(pipe_slow); +%} + instruct membar_storestore() %{ match(MemBarStoreStore); match(StoreStoreFence); diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 7c08d6de2db..3cad24d388c 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -2667,3 +2667,7 @@ void os::print_memory_mappings(char* addr, size_t bytes, outputStream* st) {} void os::jfr_report_memory_info() {} #endif // INCLUDE_JFR + +void os::print_open_file_descriptors(outputStream* st) { + // File descriptor counting not implemented on AIX +} diff --git a/src/hotspot/os/aix/porting_aix.hpp b/src/hotspot/os/aix/porting_aix.hpp index a1a22d81471..0bd71079d0a 100644 --- a/src/hotspot/os/aix/porting_aix.hpp +++ b/src/hotspot/os/aix/porting_aix.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024 SAP SE. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. 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 @@ -37,25 +37,9 @@ // (see http://linux.die.net/man/3/dladdr) // dladdr(3) is not POSIX but a GNU extension, and is not available on AIX. // -// Differences between AIX dladdr and Linux dladdr: -// -// 1) Dl_info.dli_fbase: can never work, is disabled. -// A loaded image on AIX is divided in multiple segments, at least two -// (text and data) but potentially also far more. This is because the loader may -// load each member into an own segment, as for instance happens with the libC.a -// 2) Dl_info.dli_sname: This only works for code symbols (functions); for data, a -// zero-length string is returned (""). -// 3) Dl_info.dli_saddr: For code, this will return the entry point of the function, -// not the function descriptor. -typedef struct { - const char *dli_fname; // file path of loaded library - // void *dli_fbase; - const char *dli_sname; // symbol name; "" if not known - void *dli_saddr; // address of *entry* of function; not function descriptor; -} Dl_info; +#include "dl_info.h" -// Note: we export this to use it inside J2se too #ifdef __cplusplus extern "C" #endif diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 29ebe65e0db..a4d9a2197a5 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -76,6 +76,7 @@ # include # include # include +# include # include # include # include @@ -102,6 +103,7 @@ #endif #ifdef __APPLE__ + #include #include #include #endif @@ -2596,3 +2598,45 @@ bool os::pd_dll_unload(void* libhandle, char* ebuf, int ebuflen) { return res; } // end: os::pd_dll_unload() + +void os::print_open_file_descriptors(outputStream* st) { +#ifdef __APPLE__ + char buf[1024 * sizeof(struct proc_fdinfo)]; + os::Bsd::print_open_file_descriptors(st, buf, sizeof(buf)); +#else + st->print_cr("Open File Descriptors: unknown"); +#endif +} + +void os::Bsd::print_open_file_descriptors(outputStream* st, char* buf, size_t buflen) { +#ifdef __APPLE__ + pid_t my_pid; + + // ensure the scratch buffer is big enough for at least one FD info struct + precond(buflen >= sizeof(struct proc_fdinfo)); + kern_return_t kres = pid_for_task(mach_task_self(), &my_pid); + if (kres != KERN_SUCCESS) { + st->print_cr("Open File Descriptors: unknown"); + return; + } + size_t max_fds = buflen / sizeof(struct proc_fdinfo); + struct proc_fdinfo* fds = reinterpret_cast(buf); + + // fill our buffer with FD info, up to the available buffer size + int res = proc_pidinfo(my_pid, PROC_PIDLISTFDS, 0, fds, max_fds * sizeof(struct proc_fdinfo)); + if (res <= 0) { + st->print_cr("Open File Descriptors: unknown"); + return; + } + + // print lower threshold if count exceeds buffer size + int nfiles = res / sizeof(struct proc_fdinfo); + if ((size_t)nfiles >= max_fds) { + st->print_cr("Open File Descriptors: > %zu", max_fds); + return; + } + st->print_cr("Open File Descriptors: %d", nfiles); +#else + st->print_cr("Open File Descriptors: unknown"); +#endif +} diff --git a/src/hotspot/os/bsd/os_bsd.hpp b/src/hotspot/os/bsd/os_bsd.hpp index da73211b9a7..e87a680b2d2 100644 --- a/src/hotspot/os/bsd/os_bsd.hpp +++ b/src/hotspot/os/bsd/os_bsd.hpp @@ -123,6 +123,8 @@ class os::Bsd { static int get_node_by_cpu(int cpu_id); static void print_uptime_info(outputStream* st); + static void print_open_file_descriptors(outputStream* st, char* buf, size_t buflen); + static void print_open_file_descriptors(outputStream* st); }; #endif // OS_BSD_OS_BSD_HPP diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index e49d070890e..13a005591fb 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -28,7 +28,6 @@ #include "cgroupV2Subsystem_linux.hpp" #include "logging/log.hpp" #include "memory/allocation.hpp" -#include "os_linux.hpp" #include "runtime/globals.hpp" #include "runtime/os.hpp" #include "utilities/globalDefinitions.hpp" @@ -605,6 +604,11 @@ void CgroupSubsystemFactory::cleanup(CgroupInfo* cg_infos) { } } +void CgroupSubsystem::adjust_controllers(physical_memory_size_type upper_mem_bound, int upper_cpu_bound) { + CgroupUtil::adjust_controller(memory_controller()->controller(), upper_mem_bound); + CgroupUtil::adjust_controller(cpu_controller()->controller(), upper_cpu_bound); +} + /* active_processor_count * * Calculate an appropriate number of active processors for the @@ -631,7 +635,7 @@ void CgroupSubsystemFactory::cleanup(CgroupInfo* cg_infos) { * return: * true if there were no errors. false otherwise. */ -bool CgroupSubsystem::active_processor_count(double& value) { +bool CgroupSubsystem::active_processor_count(int (*cpu_bound_func)(), double& value) { // We use a cache with a timeout to avoid performing expensive // computations in the event this function is called frequently. // [See 8227006]. @@ -643,7 +647,7 @@ bool CgroupSubsystem::active_processor_count(double& value) { return true; } - int cpu_count = os::Linux::active_processor_count(); + int cpu_count = cpu_bound_func(); double result = -1; if (!CgroupUtil::processor_count(contrl->controller(), cpu_count, result)) { return false; diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index d083a9985c2..adde37e1c77 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -278,7 +278,7 @@ class CgroupMemoryController: public CHeapObj { class CgroupSubsystem: public CHeapObj { public: bool memory_limit_in_bytes(physical_memory_size_type upper_bound, physical_memory_size_type& value); - bool active_processor_count(double& value); + bool active_processor_count(int (*cpu_bound_func)(), double& value); virtual bool pids_max(uint64_t& value) = 0; virtual bool pids_current(uint64_t& value) = 0; @@ -291,6 +291,8 @@ class CgroupSubsystem: public CHeapObj { virtual CachingCgroupController* cpu_controller() = 0; virtual CgroupCpuacctController* cpuacct_controller() = 0; + void adjust_controllers(physical_memory_size_type upper_mem_bound, int upper_cpu_bound); + bool cpu_quota(int& value); bool cpu_period(int& value); bool cpu_shares(int& value); diff --git a/src/hotspot/os/linux/cgroupUtil_linux.cpp b/src/hotspot/os/linux/cgroupUtil_linux.cpp index 570b335940b..f166f6cd5e4 100644 --- a/src/hotspot/os/linux/cgroupUtil_linux.cpp +++ b/src/hotspot/os/linux/cgroupUtil_linux.cpp @@ -24,7 +24,6 @@ */ #include "cgroupUtil_linux.hpp" -#include "os_linux.hpp" bool CgroupUtil::processor_count(CgroupCpuController* cpu_ctrl, int upper_bound, double& value) { assert(upper_bound > 0, "upper bound of cpus must be positive"); @@ -82,7 +81,7 @@ double CgroupUtil::get_updated_cpu_limit(CgroupCpuController* cpu, return lowest; } -void CgroupUtil::adjust_controller(CgroupMemoryController* mem) { +void CgroupUtil::adjust_controller(CgroupMemoryController* mem, physical_memory_size_type upper_bound) { assert(mem->cgroup_path() != nullptr, "invariant"); if (strstr(mem->cgroup_path(), "../") != nullptr) { log_warning(os, container)("Cgroup memory controller path at '%s' seems to have moved " @@ -100,17 +99,16 @@ void CgroupUtil::adjust_controller(CgroupMemoryController* mem) { char* cg_path = os::strdup(orig); char* last_slash; assert(cg_path[0] == '/', "cgroup path must start with '/'"); - physical_memory_size_type phys_mem = os::Linux::physical_memory(); char* limit_cg_path = nullptr; physical_memory_size_type limit = value_unlimited; - physical_memory_size_type lowest_limit = phys_mem; - lowest_limit = get_updated_mem_limit(mem, lowest_limit, phys_mem); - physical_memory_size_type orig_limit = lowest_limit != phys_mem ? lowest_limit : phys_mem; + physical_memory_size_type lowest_limit = upper_bound; + lowest_limit = get_updated_mem_limit(mem, lowest_limit, upper_bound); + physical_memory_size_type orig_limit = lowest_limit != upper_bound ? lowest_limit : upper_bound; while ((last_slash = strrchr(cg_path, '/')) != cg_path) { *last_slash = '\0'; // strip path // update to shortened path and try again mem->set_subsystem_path(cg_path); - limit = get_updated_mem_limit(mem, lowest_limit, phys_mem); + limit = get_updated_mem_limit(mem, lowest_limit, upper_bound); if (limit < lowest_limit) { lowest_limit = limit; os::free(limit_cg_path); // handles nullptr @@ -119,13 +117,13 @@ void CgroupUtil::adjust_controller(CgroupMemoryController* mem) { } // need to check limit at mount point mem->set_subsystem_path("/"); - limit = get_updated_mem_limit(mem, lowest_limit, phys_mem); + limit = get_updated_mem_limit(mem, lowest_limit, upper_bound); if (limit < lowest_limit) { lowest_limit = limit; os::free(limit_cg_path); // handles nullptr limit_cg_path = os::strdup("/"); } - assert(lowest_limit <= phys_mem, "limit must not exceed host memory"); + assert(lowest_limit <= upper_bound, "limit must not exceed upper bound"); if (lowest_limit != orig_limit) { // we've found a lower limit anywhere in the hierarchy, // set the path to the limit path @@ -147,7 +145,7 @@ void CgroupUtil::adjust_controller(CgroupMemoryController* mem) { os::free(limit_cg_path); } -void CgroupUtil::adjust_controller(CgroupCpuController* cpu) { +void CgroupUtil::adjust_controller(CgroupCpuController* cpu, int upper_bound) { assert(cpu->cgroup_path() != nullptr, "invariant"); if (strstr(cpu->cgroup_path(), "../") != nullptr) { log_warning(os, container)("Cgroup cpu controller path at '%s' seems to have moved " @@ -165,17 +163,16 @@ void CgroupUtil::adjust_controller(CgroupCpuController* cpu) { char* cg_path = os::strdup(orig); char* last_slash; assert(cg_path[0] == '/', "cgroup path must start with '/'"); - int host_cpus = os::Linux::active_processor_count(); - int lowest_limit = host_cpus; - double cpus = get_updated_cpu_limit(cpu, lowest_limit, host_cpus); - int orig_limit = lowest_limit != host_cpus ? lowest_limit : host_cpus; + int lowest_limit = upper_bound; + double cpus = get_updated_cpu_limit(cpu, lowest_limit, upper_bound); + int orig_limit = lowest_limit != upper_bound ? lowest_limit : upper_bound; char* limit_cg_path = nullptr; while ((last_slash = strrchr(cg_path, '/')) != cg_path) { *last_slash = '\0'; // strip path // update to shortened path and try again cpu->set_subsystem_path(cg_path); - cpus = get_updated_cpu_limit(cpu, lowest_limit, host_cpus); - if (cpus != host_cpus && cpus < lowest_limit) { + cpus = get_updated_cpu_limit(cpu, lowest_limit, upper_bound); + if (cpus != upper_bound && cpus < lowest_limit) { lowest_limit = cpus; os::free(limit_cg_path); // handles nullptr limit_cg_path = os::strdup(cg_path); @@ -183,8 +180,8 @@ void CgroupUtil::adjust_controller(CgroupCpuController* cpu) { } // need to check limit at mount point cpu->set_subsystem_path("/"); - cpus = get_updated_cpu_limit(cpu, lowest_limit, host_cpus); - if (cpus != host_cpus && cpus < lowest_limit) { + cpus = get_updated_cpu_limit(cpu, lowest_limit, upper_bound); + if (cpus != upper_bound && cpus < lowest_limit) { lowest_limit = cpus; os::free(limit_cg_path); // handles nullptr limit_cg_path = os::strdup(cg_path); diff --git a/src/hotspot/os/linux/cgroupUtil_linux.hpp b/src/hotspot/os/linux/cgroupUtil_linux.hpp index 1fd2a7d872b..68585c22c2d 100644 --- a/src/hotspot/os/linux/cgroupUtil_linux.hpp +++ b/src/hotspot/os/linux/cgroupUtil_linux.hpp @@ -35,10 +35,10 @@ class CgroupUtil: AllStatic { static bool processor_count(CgroupCpuController* cpu, int upper_bound, double& value); // Given a memory controller, adjust its path to a point in the hierarchy // that represents the closest memory limit. - static void adjust_controller(CgroupMemoryController* m); + static void adjust_controller(CgroupMemoryController* m, physical_memory_size_type upper_bound); // Given a cpu controller, adjust its path to a point in the hierarchy // that represents the closest cpu limit. - static void adjust_controller(CgroupCpuController* c); + static void adjust_controller(CgroupCpuController* c, int upper_bound); private: static physical_memory_size_type get_updated_mem_limit(CgroupMemoryController* m, physical_memory_size_type lowest, diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index c8f5a290c99..e42b7a13391 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -326,8 +326,6 @@ CgroupV1Subsystem::CgroupV1Subsystem(CgroupV1Controller* cpuset, _cpuset(cpuset), _cpuacct(cpuacct), _pids(pids) { - CgroupUtil::adjust_controller(memory); - CgroupUtil::adjust_controller(cpu); _memory = new CachingCgroupController(memory); _cpu = new CachingCgroupController(cpu); } diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index 30e1affc646..edd80bb7427 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -154,8 +154,6 @@ CgroupV2Subsystem::CgroupV2Subsystem(CgroupV2MemoryController * memory, CgroupV2CpuacctController* cpuacct, CgroupV2Controller unified) : _unified(unified) { - CgroupUtil::adjust_controller(memory); - CgroupUtil::adjust_controller(cpu); _memory = new CachingCgroupController(memory); _cpu = new CachingCgroupController(cpu); _cpuacct = cpuacct; diff --git a/src/hotspot/os/linux/hugepages.cpp b/src/hotspot/os/linux/hugepages.cpp index 5472c093d3f..1340c470dff 100644 --- a/src/hotspot/os/linux/hugepages.cpp +++ b/src/hotspot/os/linux/hugepages.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, 2024, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -35,11 +35,16 @@ #include ExplicitHugePageSupport::ExplicitHugePageSupport() : - _initialized(false), _pagesizes(), _default_hugepage_size(SIZE_MAX), _inconsistent(false) {} + _initialized{false}, _os_supported{}, _pre_allocated{}, _default_hugepage_size{SIZE_MAX}, _inconsistent{false} {} -os::PageSizes ExplicitHugePageSupport::pagesizes() const { +os::PageSizes ExplicitHugePageSupport::os_supported() const { assert(_initialized, "Not initialized"); - return _pagesizes; + return _os_supported; +} + +os::PageSizes ExplicitHugePageSupport::pre_allocated() const { + assert(_initialized, "Not initialized"); + return _pre_allocated; } size_t ExplicitHugePageSupport::default_hugepage_size() const { @@ -129,10 +134,24 @@ static os::PageSizes scan_hugepages() { return pagesizes; } +static os::PageSizes filter_pre_allocated_hugepages(os::PageSizes pagesizes) { + os::PageSizes pre_allocated{}; + char filename[PATH_MAX]; + for (size_t ps = pagesizes.smallest(); ps != 0; ps = pagesizes.next_larger(ps)) { + os::snprintf_checked(filename, sizeof(filename), "%s/hugepages-%zukB/nr_hugepages", sys_hugepages, ps / K); + size_t pages; + bool read_success = read_number_file(filename, &pages); + if (read_success && pages > 0) { + pre_allocated.add(ps); + } + } + return pre_allocated; +} + void ExplicitHugePageSupport::print_on(outputStream* os) { if (_initialized) { os->print_cr("Explicit hugepage support:"); - for (size_t s = _pagesizes.smallest(); s != 0; s = _pagesizes.next_larger(s)) { + for (size_t s = _os_supported.smallest(); s != 0; s = _os_supported.next_larger(s)) { os->print_cr(" hugepage size: " EXACTFMT, EXACTFMTARGS(s)); } os->print_cr(" default hugepage size: " EXACTFMT, EXACTFMTARGS(_default_hugepage_size)); @@ -147,14 +166,15 @@ void ExplicitHugePageSupport::print_on(outputStream* os) { void ExplicitHugePageSupport::scan_os() { _default_hugepage_size = scan_default_hugepagesize(); if (_default_hugepage_size > 0) { - _pagesizes = scan_hugepages(); + _os_supported = scan_hugepages(); + _pre_allocated = filter_pre_allocated_hugepages(_os_supported); // See https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt: /proc/meminfo should match // /sys/kernel/mm/hugepages/hugepages-xxxx. However, we may run on a broken kernel (e.g. on WSL) // that only exposes /proc/meminfo but not /sys/kernel/mm/hugepages. In that case, we are not // sure about the state of hugepage support by the kernel, so we won't use explicit hugepages. - if (!_pagesizes.contains(_default_hugepage_size)) { + if (!_os_supported.contains(_default_hugepage_size)) { log_info(pagesize)("Unexpected configuration: default pagesize (%zu) " - "has no associated directory in /sys/kernel/mm/hugepages..", _default_hugepage_size); + "has no associated directory in /sys/kernel/mm/hugepages.", _default_hugepage_size); _inconsistent = true; } } diff --git a/src/hotspot/os/linux/hugepages.hpp b/src/hotspot/os/linux/hugepages.hpp index efd27c55fd6..5a9767b4ff8 100644 --- a/src/hotspot/os/linux/hugepages.hpp +++ b/src/hotspot/os/linux/hugepages.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, 2024, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -45,7 +45,10 @@ class ExplicitHugePageSupport { // All supported hugepage sizes (sizes for which entries exist // in /sys/kernel/mm/hugepages/hugepage-xxx) - os::PageSizes _pagesizes; + os::PageSizes _os_supported; + + // Above pages filtered for where the contents of file nr_hugepages was larger than zero + os::PageSizes _pre_allocated; // Contains the default hugepage. The "default hugepage size" is the one that // - is marked in /proc/meminfo as "Hugepagesize" @@ -60,7 +63,8 @@ public: void scan_os(); - os::PageSizes pagesizes() const; + os::PageSizes os_supported() const; + os::PageSizes pre_allocated() const; size_t default_hugepage_size() const; void print_on(outputStream* os); diff --git a/src/hotspot/os/linux/osContainer_linux.cpp b/src/hotspot/os/linux/osContainer_linux.cpp index b46263efd99..da2cbf381e6 100644 --- a/src/hotspot/os/linux/osContainer_linux.cpp +++ b/src/hotspot/os/linux/osContainer_linux.cpp @@ -59,6 +59,11 @@ void OSContainer::init() { if (cgroup_subsystem == nullptr) { return; // Required subsystem files not found or other error } + // Adjust controller paths once subsystem is initialized + physical_memory_size_type phys_mem = os::Linux::physical_memory(); + int host_cpus = os::Linux::active_processor_count(); + cgroup_subsystem->adjust_controllers(phys_mem, host_cpus); + /* * In order to avoid a false positive on is_containerized() on * Linux systems outside a container *and* to ensure compatibility @@ -252,7 +257,7 @@ char * OSContainer::cpu_cpuset_memory_nodes() { bool OSContainer::active_processor_count(double& value) { assert(cgroup_subsystem != nullptr, "cgroup subsystem not available"); - return cgroup_subsystem->active_processor_count(value); + return cgroup_subsystem->active_processor_count(&os::Linux::active_processor_count, value); } bool OSContainer::cpu_quota(int& value) { diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 9c2fbab7535..c79b0ab9fb5 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -83,6 +83,7 @@ #endif # include +# include # include # include # include @@ -113,6 +114,7 @@ # include # include # include +# include # include #ifdef __GLIBC__ # include @@ -2161,6 +2163,8 @@ void os::print_os_info(outputStream* st) { os::Posix::print_rlimit_info(st); + os::print_open_file_descriptors(st); + os::Posix::print_load_average(st); st->cr(); @@ -3814,8 +3818,8 @@ static int hugetlbfs_page_size_flag(size_t page_size) { } static bool hugetlbfs_sanity_check(size_t page_size) { - const os::PageSizes page_sizes = HugePages::explicit_hugepage_info().pagesizes(); - assert(page_sizes.contains(page_size), "Invalid page sizes passed"); + const os::PageSizes os_supported = HugePages::explicit_hugepage_info().os_supported(); + assert(os_supported.contains(page_size), "Invalid page sizes passed (%zu)", page_size); // Include the page size flag to ensure we sanity check the correct page size. int flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB | hugetlbfs_page_size_flag(page_size); @@ -3829,16 +3833,16 @@ static bool hugetlbfs_sanity_check(size_t page_size) { log_info(pagesize)("Large page size (" EXACTFMT ") failed sanity check, " "checking if smaller large page sizes are usable", EXACTFMTARGS(page_size)); - for (size_t page_size_ = page_sizes.next_smaller(page_size); - page_size_ > os::vm_page_size(); - page_size_ = page_sizes.next_smaller(page_size_)) { - flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB | hugetlbfs_page_size_flag(page_size_); - p = mmap(nullptr, page_size_, PROT_READ|PROT_WRITE, flags, -1, 0); + for (size_t size = os_supported.next_smaller(page_size); + size > os::vm_page_size(); + size = os_supported.next_smaller(size)) { + flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB | hugetlbfs_page_size_flag(size); + p = mmap(nullptr, size, PROT_READ|PROT_WRITE, flags, -1, 0); if (p != MAP_FAILED) { // Mapping succeeded, sanity check passed. - munmap(p, page_size_); + munmap(p, size); log_info(pagesize)("Large page size (" EXACTFMT ") passed sanity check", - EXACTFMTARGS(page_size_)); + EXACTFMTARGS(size)); return true; } } @@ -4020,7 +4024,7 @@ void os::Linux::large_page_init() { // - os::large_page_size() is the default explicit hugepage size (/proc/meminfo "Hugepagesize") // - os::pagesizes() contains all hugepage sizes the kernel supports, regardless whether there // are pages configured in the pool or not (from /sys/kernel/hugepages/hugepage-xxxx ...) - os::PageSizes all_large_pages = HugePages::explicit_hugepage_info().pagesizes(); + os::PageSizes all_large_pages = HugePages::explicit_hugepage_info().os_supported(); const size_t default_large_page_size = HugePages::default_explicit_hugepage_size(); // 3) Consistency check and post-processing @@ -4062,10 +4066,10 @@ void os::Linux::large_page_init() { _large_page_size = large_page_size; - // Populate _page_sizes with large page sizes less than or equal to - // _large_page_size. - for (size_t page_size = _large_page_size; page_size != 0; - page_size = all_large_pages.next_smaller(page_size)) { + // Populate _page_sizes with _large_page_size (default large page size) even if not pre-allocated. + // Then, populate _page_sizes with all smaller large page sizes that have been pre-allocated. + os::PageSizes pre_allocated = HugePages::explicit_hugepage_info().pre_allocated(); + for (size_t page_size = _large_page_size; page_size != 0; page_size = pre_allocated.next_smaller(page_size)) { _page_sizes.add(page_size); } } @@ -4129,12 +4133,12 @@ static char* reserve_memory_special_huge_tlbfs(size_t bytes, size_t page_size, char* req_addr, bool exec) { - const os::PageSizes page_sizes = HugePages::explicit_hugepage_info().pagesizes(); + const os::PageSizes os_supported = HugePages::explicit_hugepage_info().os_supported(); assert(UseLargePages, "only for Huge TLBFS large pages"); assert(is_aligned(req_addr, alignment), "Must be"); assert(is_aligned(req_addr, page_size), "Must be"); assert(is_aligned(alignment, os::vm_allocation_granularity()), "Must be"); - assert(page_sizes.contains(page_size), "Must be a valid page size"); + assert(os_supported.contains(page_size), "Must be a valid page size"); assert(page_size > os::vm_page_size(), "Must be a large page size"); assert(bytes >= page_size, "Shouldn't allocate large pages for small sizes"); @@ -4549,6 +4553,7 @@ void os::Linux::numa_init() { FLAG_SET_ERGO_IF_DEFAULT(UseNUMAInterleaving, true); } +#if INCLUDE_PARALLELGC if (UseParallelGC && UseNUMA && UseLargePages && !can_commit_large_page_memory()) { // With static large pages we cannot uncommit a page, so there's no way // we can make the adaptive lgrp chunk resizing work. If the user specified both @@ -4560,6 +4565,7 @@ void os::Linux::numa_init() { UseAdaptiveNUMAChunkSizing = false; } } +#endif } void os::Linux::disable_numa(const char* reason, bool warning) { @@ -5427,3 +5433,31 @@ bool os::pd_dll_unload(void* libhandle, char* ebuf, int ebuflen) { return res; } // end: os::pd_dll_unload() + +void os::print_open_file_descriptors(outputStream* st) { + DIR* dirp = opendir("/proc/self/fd"); + int fds = 0; + struct dirent* dentp; + const jlong TIMEOUT_NS = 50000000L; // 50 ms in nanoseconds + bool timed_out = false; + + // limit proc file read to 50ms + jlong start = os::javaTimeNanos(); + assert(dirp != nullptr, "No proc fs?"); + while ((dentp = readdir(dirp)) != nullptr && !timed_out) { + if (isdigit(dentp->d_name[0])) fds++; + if (fds % 100 == 0) { + jlong now = os::javaTimeNanos(); + if ((now - start) > TIMEOUT_NS) { + timed_out = true; + } + } + } + + closedir(dirp); + if (timed_out) { + st->print_cr("Open File Descriptors: > %d", fds); + } else { + st->print_cr("Open File Descriptors: %d", fds); + } +} diff --git a/src/hotspot/os/posix/dtrace/hotspot_jni.d b/src/hotspot/os/posix/dtrace/hotspot_jni.d index c5676921b37..1937769dcb2 100644 --- a/src/hotspot/os/posix/dtrace/hotspot_jni.d +++ b/src/hotspot/os/posix/dtrace/hotspot_jni.d @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -366,6 +366,8 @@ provider hotspot_jni { probe IsInstanceOf__return(uintptr_t); probe IsSameObject__entry(void*, void*, void*); probe IsSameObject__return(uintptr_t); + probe IsVirtualThread__entry(void*, void*); + probe IsVirtualThread__return(uintptr_t); probe MonitorEnter__entry(void*, void*); probe MonitorEnter__return(uint32_t); probe MonitorExit__entry(void*, void*); diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index f147ed4be93..1fb2a248bec 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -888,6 +888,14 @@ void* os::lookup_function(const char* name) { return dlsym(RTLD_DEFAULT, name); } +int64_t os::ftell(FILE* file) { + return ::ftell(file); +} + +int os::fseek(FILE* file, int64_t offset, int whence) { + return ::fseek(file, offset, whence); +} + jlong os::lseek(int fd, jlong offset, int whence) { return (jlong) ::lseek(fd, offset, whence); } diff --git a/src/hotspot/os/posix/perfMemory_posix.cpp b/src/hotspot/os/posix/perfMemory_posix.cpp index d9bde6fa825..b8be77c5e05 100644 --- a/src/hotspot/os/posix/perfMemory_posix.cpp +++ b/src/hotspot/os/posix/perfMemory_posix.cpp @@ -1084,18 +1084,9 @@ static char* mmap_create_shared(size_t size) { // release a named shared memory region that was mmap-ed. // static void unmap_shared(char* addr, size_t bytes) { - int res; - if (MemTracker::enabled()) { - MemTracker::NmtVirtualMemoryLocker nvml; - res = ::munmap(addr, bytes); - if (res == 0) { - MemTracker::record_virtual_memory_release(addr, bytes); - } - } else { - res = ::munmap(addr, bytes); - } - if (res != 0) { - log_info(os)("os::release_memory failed (" PTR_FORMAT ", %zu)", p2i(addr), bytes); + MemTracker::record_virtual_memory_release(addr, bytes); + if (::munmap(addr, bytes) != 0) { + fatal("os::release_memory failed (" PTR_FORMAT ", %zu)", p2i(addr), bytes); } } diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 76f47640e5a..18d047348cb 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -5114,6 +5114,13 @@ jlong os::seek_to_file_offset(int fd, jlong offset) { return (jlong)::_lseeki64(fd, (__int64)offset, SEEK_SET); } +int64_t os::ftell(FILE* file) { + return ::_ftelli64(file); +} + +int os::fseek(FILE* file, int64_t offset, int whence) { + return ::_fseeki64(file,offset, whence); +} jlong os::lseek(int fd, jlong offset, int whence) { return (jlong) ::_lseeki64(fd, offset, whence); @@ -6276,6 +6283,10 @@ const void* os::get_saved_assert_context(const void** sigInfo) { return nullptr; } +void os::print_open_file_descriptors(outputStream* st) { + // File descriptor counting not supported on Windows. +} + /* * Windows/x64 does not use stack frames the way expected by Java: * [1] in most cases, there is no frame pointer. All locals are addressed via RSP diff --git a/src/hotspot/os/windows/perfMemory_windows.cpp b/src/hotspot/os/windows/perfMemory_windows.cpp index f54a2b52cca..dad2804f18a 100644 --- a/src/hotspot/os/windows/perfMemory_windows.cpp +++ b/src/hotspot/os/windows/perfMemory_windows.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -1682,12 +1682,7 @@ void PerfMemory::detach(char* addr, size_t bytes) { return; } - if (MemTracker::enabled()) { - // it does not go through os api, the operation has to record from here - MemTracker::NmtVirtualMemoryLocker nvml; - remove_file_mapping(addr); - MemTracker::record_virtual_memory_release(addr, bytes); - } else { - remove_file_mapping(addr); - } + // it does not go through os api, the operation has to record from here + MemTracker::record_virtual_memory_release(addr, bytes); + remove_file_mapping(addr); } diff --git a/src/hotspot/os_cpu/bsd_zero/atomicAccess_bsd_zero.hpp b/src/hotspot/os_cpu/bsd_zero/atomicAccess_bsd_zero.hpp index 6c8684718fc..8e45490e5b6 100644 --- a/src/hotspot/os_cpu/bsd_zero/atomicAccess_bsd_zero.hpp +++ b/src/hotspot/os_cpu/bsd_zero/atomicAccess_bsd_zero.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright 2007, 2008, 2011, 2015, Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,7 +27,6 @@ #define OS_CPU_BSD_ZERO_ATOMICACCESS_BSD_ZERO_HPP #include "orderAccess_bsd_zero.hpp" -#include "runtime/os.hpp" // Implementation of class AtomicAccess diff --git a/src/hotspot/os_cpu/linux_arm/atomicAccess_linux_arm.hpp b/src/hotspot/os_cpu/linux_arm/atomicAccess_linux_arm.hpp index 390207f9e5e..c03f5ed1c8b 100644 --- a/src/hotspot/os_cpu/linux_arm/atomicAccess_linux_arm.hpp +++ b/src/hotspot/os_cpu/linux_arm/atomicAccess_linux_arm.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, 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 @@ -26,8 +26,6 @@ #define OS_CPU_LINUX_ARM_ATOMICACCESS_LINUX_ARM_HPP #include "memory/allStatic.hpp" -#include "runtime/os.hpp" -#include "runtime/vm_version.hpp" // Implementation of class AtomicAccess diff --git a/src/hotspot/os_cpu/linux_arm/orderAccess_linux_arm.hpp b/src/hotspot/os_cpu/linux_arm/orderAccess_linux_arm.hpp index 3bb357704fb..49c6942b8e0 100644 --- a/src/hotspot/os_cpu/linux_arm/orderAccess_linux_arm.hpp +++ b/src/hotspot/os_cpu/linux_arm/orderAccess_linux_arm.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, 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,6 @@ // Included in orderAccess.hpp header file. -#include "runtime/os.hpp" #include "runtime/vm_version.hpp" // Implementation of class OrderAccess. diff --git a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp index 35cbb75e8ff..648131b94a3 100644 --- a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp +++ b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp @@ -36,40 +36,42 @@ #include #include +static constexpr uint64_t feature_bit(int n) { return nth_bit(n); } + #ifndef HWCAP_ISA_I -#define HWCAP_ISA_I nth_bit('I' - 'A') +#define HWCAP_ISA_I feature_bit('I' - 'A') #endif #ifndef HWCAP_ISA_M -#define HWCAP_ISA_M nth_bit('M' - 'A') +#define HWCAP_ISA_M feature_bit('M' - 'A') #endif #ifndef HWCAP_ISA_A -#define HWCAP_ISA_A nth_bit('A' - 'A') +#define HWCAP_ISA_A feature_bit('A' - 'A') #endif #ifndef HWCAP_ISA_F -#define HWCAP_ISA_F nth_bit('F' - 'A') +#define HWCAP_ISA_F feature_bit('F' - 'A') #endif #ifndef HWCAP_ISA_D -#define HWCAP_ISA_D nth_bit('D' - 'A') +#define HWCAP_ISA_D feature_bit('D' - 'A') #endif #ifndef HWCAP_ISA_C -#define HWCAP_ISA_C nth_bit('C' - 'A') +#define HWCAP_ISA_C feature_bit('C' - 'A') #endif #ifndef HWCAP_ISA_Q -#define HWCAP_ISA_Q nth_bit('Q' - 'A') +#define HWCAP_ISA_Q feature_bit('Q' - 'A') #endif #ifndef HWCAP_ISA_H -#define HWCAP_ISA_H nth_bit('H' - 'A') +#define HWCAP_ISA_H feature_bit('H' - 'A') #endif #ifndef HWCAP_ISA_V -#define HWCAP_ISA_V nth_bit('V' - 'A') +#define HWCAP_ISA_V feature_bit('V' - 'A') #endif #define read_csr(csr) \ diff --git a/src/hotspot/os_cpu/linux_s390/atomicAccess_linux_s390.hpp b/src/hotspot/os_cpu/linux_s390/atomicAccess_linux_s390.hpp index f3c1e8f1a2c..492ccf73bdf 100644 --- a/src/hotspot/os_cpu/linux_s390/atomicAccess_linux_s390.hpp +++ b/src/hotspot/os_cpu/linux_s390/atomicAccess_linux_s390.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,10 +26,6 @@ #ifndef OS_CPU_LINUX_S390_ATOMICACCESS_LINUX_S390_HPP #define OS_CPU_LINUX_S390_ATOMICACCESS_LINUX_S390_HPP -#include "runtime/atomicAccess.hpp" -#include "runtime/os.hpp" -#include "runtime/vm_version.hpp" - // Note that the compare-and-swap instructions on System z perform // a serialization function before the storage operand is fetched // and again after the operation is completed. diff --git a/src/hotspot/os_cpu/windows_aarch64/atomicAccess_windows_aarch64.hpp b/src/hotspot/os_cpu/windows_aarch64/atomicAccess_windows_aarch64.hpp index f8119654c50..9238043f7a4 100644 --- a/src/hotspot/os_cpu/windows_aarch64/atomicAccess_windows_aarch64.hpp +++ b/src/hotspot/os_cpu/windows_aarch64/atomicAccess_windows_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Microsoft Corporation. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,9 +27,7 @@ #define OS_CPU_WINDOWS_AARCH64_ATOMICACCESS_WINDOWS_AARCH64_HPP #include -#include "runtime/os.hpp" -#include "runtime/vm_version.hpp" - +#include // As per atomicAccess.hpp all read-modify-write operations have to provide two-way // barriers semantics. The memory_order parameter is ignored - we always provide diff --git a/src/hotspot/os_cpu/windows_x86/atomicAccess_windows_x86.hpp b/src/hotspot/os_cpu/windows_x86/atomicAccess_windows_x86.hpp index aa78a401235..252411f62bc 100644 --- a/src/hotspot/os_cpu/windows_x86/atomicAccess_windows_x86.hpp +++ b/src/hotspot/os_cpu/windows_x86/atomicAccess_windows_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -26,7 +26,7 @@ #define OS_CPU_WINDOWS_X86_ATOMICACCESS_WINDOWS_X86_HPP #include -#include "runtime/os.hpp" +#include // Note that in MSVC, volatile memory accesses are explicitly // guaranteed to have acquire release semantics (w.r.t. compiler diff --git a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp index 182587d2f2f..4dd2bff7c89 100644 --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -4276,7 +4276,9 @@ bool MatchRule::is_ideal_membar() const { !strcmp(_opType,"LoadFence" ) || !strcmp(_opType,"StoreFence") || !strcmp(_opType,"StoreStoreFence") || + !strcmp(_opType,"MemBarStoreLoad") || !strcmp(_opType,"MemBarVolatile") || + !strcmp(_opType,"MemBarFull") || !strcmp(_opType,"MemBarCPUOrder") || !strcmp(_opType,"MemBarStoreStore") || !strcmp(_opType,"OnSpinWait"); diff --git a/src/hotspot/share/cds/aotClassInitializer.cpp b/src/hotspot/share/cds/aotClassInitializer.cpp index 06fc3af6f30..41fdeb537cc 100644 --- a/src/hotspot/share/cds/aotClassInitializer.cpp +++ b/src/hotspot/share/cds/aotClassInitializer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -234,7 +234,8 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) { } void AOTClassInitializer::call_runtime_setup(JavaThread* current, InstanceKlass* ik) { - assert(ik->has_aot_initialized_mirror(), "sanity"); + precond(ik->has_aot_initialized_mirror()); + precond(!AOTLinkedClassBulkLoader::is_initializing_classes_early()); if (ik->is_runtime_setup_required()) { if (log_is_enabled(Info, aot, init)) { ResourceMark rm; diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.cpp b/src/hotspot/share/cds/aotConstantPoolResolver.cpp index 93145940955..f1a704d4bee 100644 --- a/src/hotspot/share/cds/aotConstantPoolResolver.cpp +++ b/src/hotspot/share/cds/aotConstantPoolResolver.cpp @@ -81,6 +81,7 @@ bool AOTConstantPoolResolver::is_resolution_deterministic(ConstantPool* cp, int bool AOTConstantPoolResolver::is_class_resolution_deterministic(InstanceKlass* cp_holder, Klass* resolved_class) { assert(!is_in_archivebuilder_buffer(cp_holder), "sanity"); assert(!is_in_archivebuilder_buffer(resolved_class), "sanity"); + assert_at_safepoint(); // try_add_candidate() is called below and requires to be at safepoint. if (resolved_class->is_instance_klass()) { InstanceKlass* ik = InstanceKlass::cast(resolved_class); @@ -346,7 +347,15 @@ void AOTConstantPoolResolver::maybe_resolve_fmi_ref(InstanceKlass* ik, Method* m break; case Bytecodes::_invokehandle: - InterpreterRuntime::cds_resolve_invokehandle(raw_index, cp, CHECK); + if (CDSConfig::is_dumping_method_handles()) { + ResolvedMethodEntry* method_entry = cp->resolved_method_entry_at(raw_index); + int cp_index = method_entry->constant_pool_index(); + Symbol* sig = cp->uncached_signature_ref_at(cp_index); + Klass* k; + if (check_methodtype_signature(cp(), sig, &k, true)) { + InterpreterRuntime::cds_resolve_invokehandle(raw_index, cp, CHECK); + } + } break; default: @@ -400,7 +409,7 @@ void AOTConstantPoolResolver::preresolve_indy_cp_entries(JavaThread* current, In // Check the MethodType signatures used by parameters to the indy BSMs. Make sure we don't // use types that have been excluded, or else we might end up creating MethodTypes that cannot be stored // in the AOT cache. -bool AOTConstantPoolResolver::check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret) { +bool AOTConstantPoolResolver::check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret, bool is_invokehandle) { ResourceMark rm; for (SignatureStream ss(sig); !ss.is_done(); ss.next()) { if (ss.is_reference()) { @@ -413,11 +422,18 @@ bool AOTConstantPoolResolver::check_methodtype_signature(ConstantPool* cp, Symbo if (SystemDictionaryShared::should_be_excluded(k)) { if (log_is_enabled(Warning, aot, resolve)) { ResourceMark rm; - log_warning(aot, resolve)("Cannot aot-resolve Lambda proxy because %s is excluded", k->external_name()); + log_warning(aot, resolve)("Cannot aot-resolve %s because %s is excluded", + is_invokehandle ? "invokehandle" : "Lambda proxy", + k->external_name()); } return false; } + // cp->pool_holder() must be able to resolve k in production run + precond(CDSConfig::is_dumping_aot_linked_classes()); + precond(SystemDictionaryShared::is_builtin_loader(cp->pool_holder()->class_loader_data())); + precond(SystemDictionaryShared::is_builtin_loader(k->class_loader_data())); + if (ss.at_return_type() && return_type_ret != nullptr) { *return_type_ret = k; } @@ -475,11 +491,44 @@ bool AOTConstantPoolResolver::check_lambda_metafactory_methodhandle_arg(Constant return false; } + // klass and sigature of the method (no need to check the method name) Symbol* sig = cp->method_handle_signature_ref_at(mh_index); + Symbol* klass_name = cp->klass_name_at(cp->method_handle_klass_index_at(mh_index)); + if (log_is_enabled(Debug, aot, resolve)) { ResourceMark rm; log_debug(aot, resolve)("Checking MethodType of MethodHandle for LambdaMetafactory BSM arg %d: %s", arg_i, sig->as_C_string()); } + + { + Klass* k = find_loaded_class(Thread::current(), cp->pool_holder()->class_loader(), klass_name); + if (k == nullptr) { + // Dumping AOT cache: all classes should have been loaded by FinalImageRecipes::load_all_classes(). k must have + // been a class that was excluded when FinalImageRecipes recorded all classes at the end of the training run. + // + // Dumping static CDS archive: all classes in the classlist have already been loaded, before we resolve + // constants. k must have been a class that was excluded when the classlist was written + // at the end of the training run. + if (log_is_enabled(Warning, aot, resolve)) { + ResourceMark rm; + log_warning(aot, resolve)("Cannot aot-resolve Lambda proxy because %s is not loaded", klass_name->as_C_string()); + } + return false; + } + if (SystemDictionaryShared::should_be_excluded(k)) { + if (log_is_enabled(Warning, aot, resolve)) { + ResourceMark rm; + log_warning(aot, resolve)("Cannot aot-resolve Lambda proxy because %s is excluded", k->external_name()); + } + return false; + } + + // cp->pool_holder() must be able to resolve k in production run + precond(CDSConfig::is_dumping_aot_linked_classes()); + precond(SystemDictionaryShared::is_builtin_loader(cp->pool_holder()->class_loader_data())); + precond(SystemDictionaryShared::is_builtin_loader(k->class_loader_data())); + } + return check_methodtype_signature(cp, sig); } diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.hpp b/src/hotspot/share/cds/aotConstantPoolResolver.hpp index e49d9d1ad0b..ecf2ac27061 100644 --- a/src/hotspot/share/cds/aotConstantPoolResolver.hpp +++ b/src/hotspot/share/cds/aotConstantPoolResolver.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -74,7 +74,10 @@ class AOTConstantPoolResolver : AllStatic { static void maybe_resolve_fmi_ref(InstanceKlass* ik, Method* m, Bytecodes::Code bc, int raw_index, GrowableArray* resolve_fmi_list, TRAPS); - static bool check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret = nullptr); +public: + static bool check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret = nullptr, bool is_invokehandle = false); + +private: static bool check_lambda_metafactory_signature(ConstantPool* cp, Symbol* sig); static bool check_lambda_metafactory_methodtype_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i); static bool check_lambda_metafactory_methodhandle_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i); diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp index 3653f9d518c..6a60177fc40 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -116,11 +116,24 @@ void AOTLinkedClassBulkLoader::preload_classes_in_table(Array* c } } +#ifdef ASSERT +// true iff we are inside AOTLinkedClassBulkLoader::link_classes(), when +// we are moving classes into the fully_initialized state before the +// JVM is able to execute any bytecodes. +static bool _is_initializing_classes_early = false; +bool AOTLinkedClassBulkLoader::is_initializing_classes_early() { + return _is_initializing_classes_early; +} +#endif + // Some cached heap objects may hold references to methods in aot-linked // classes (via MemberName). We need to make sure all classes are // linked before executing any bytecode. void AOTLinkedClassBulkLoader::link_classes(JavaThread* current) { + DEBUG_ONLY(_is_initializing_classes_early = true); link_classes_impl(current); + DEBUG_ONLY(_is_initializing_classes_early = false); + if (current->has_pending_exception()) { exit_on_exception(current); } @@ -135,6 +148,13 @@ void AOTLinkedClassBulkLoader::link_classes_impl(TRAPS) { link_classes_in_table(table->boot2(), CHECK); link_classes_in_table(table->platform(), CHECK); link_classes_in_table(table->app(), CHECK); + + init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->boot1(), /*early_only=*/true, CHECK); + init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->boot2(), /*early_only=*/true, CHECK); + init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->platform(), /*early_only=*/true, CHECK); + init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->app(), /*early_only=*/true, CHECK); + + log_info(aot, init)("------ finished early class init"); } void AOTLinkedClassBulkLoader::link_classes_in_table(Array* classes, TRAPS) { @@ -216,7 +236,7 @@ void AOTLinkedClassBulkLoader::validate_module(Klass* k, const char* category_na #endif void AOTLinkedClassBulkLoader::init_javabase_classes(JavaThread* current) { - init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->boot1(), current); + init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->boot1(), /*early_only=*/false, current); if (current->has_pending_exception()) { exit_on_exception(current); } @@ -246,9 +266,9 @@ void AOTLinkedClassBulkLoader::init_non_javabase_classes_impl(TRAPS) { assert(h_system_loader() != nullptr, "must be"); AOTLinkedClassTable* table = AOTLinkedClassTable::get(); - init_classes_for_loader(Handle(), table->boot2(), CHECK); - init_classes_for_loader(h_platform_loader, table->platform(), CHECK); - init_classes_for_loader(h_system_loader, table->app(), CHECK); + init_classes_for_loader(Handle(), table->boot2(), /*early_only=*/false, CHECK); + init_classes_for_loader(h_platform_loader, table->platform(), /*early_only=*/false, CHECK); + init_classes_for_loader(h_system_loader, table->app(), /*early_only=*/false, CHECK); if (Universe::is_fully_initialized() && VerifyDuringStartup) { // Make sure we're still in a clean state. @@ -324,22 +344,80 @@ void AOTLinkedClassBulkLoader::initiate_loading(JavaThread* current, const char* } } -// Some AOT-linked classes for must be initialized early. This includes -// - classes that were AOT-initialized by AOTClassInitializer -// - the classes of all objects that are reachable from the archived mirrors of -// the AOT-linked classes for . -void AOTLinkedClassBulkLoader::init_classes_for_loader(Handle class_loader, Array* classes, TRAPS) { +// Can we move ik into fully_initialized state before the JVM is able to execute +// bytecodes? +static bool is_early_init_possible(InstanceKlass* ik) { + if (ik->is_runtime_setup_required()) { + // Bytecodes need to be executed in order to initialize this class. + if (log_is_enabled(Debug, aot, init)) { + ResourceMark rm; + log_debug(aot, init)("No early init %s: needs runtimeSetup()", + ik->external_name()); + } + return false; + } + + if (ik->super() != nullptr && !ik->super()->is_initialized()) { + // is_runtime_setup_required() == true for a super type + if (log_is_enabled(Debug, aot, init)) { + ResourceMark rm; + log_debug(aot, init)("No early init %s: super type %s not initialized", + ik->external_name(), ik->super()->external_name()); + } + return false; + } + + Array* interfaces = ik->local_interfaces(); + int num_interfaces = interfaces->length(); + for (int i = 0; i < num_interfaces; i++) { + InstanceKlass* intf = interfaces->at(i); + if (!intf->is_initialized() && intf->interface_needs_clinit_execution_as_super(/*also_check_supers*/false)) { + // is_runtime_setup_required() == true for this interface + if (log_is_enabled(Debug, aot, init)) { + ResourceMark rm; + log_debug(aot, init)("No early init %s: interface type %s not initialized", + ik->external_name(), intf->external_name()); + } + return false; + } + } + + return true; +} + +// Normally, classes are initialized on demand. However, some AOT-linked classes +// for the class_loader must be proactively intialized, including: +// - Classes that have an AOT-initialized mirror (they were AOT-initialized by +// AOTClassInitializer during the assembly phase). +// - The classes of all objects that are reachable from the archived mirrors of +// the AOT-linked classes for the class_loader. These are recorded in the special +// subgraph. +// +// (early_only == true) means that this function is called before the JVM +// is capable of executing Java bytecodes. +void AOTLinkedClassBulkLoader::init_classes_for_loader(Handle class_loader, Array* classes, + bool early_only, TRAPS) { if (classes != nullptr) { for (int i = 0; i < classes->length(); i++) { InstanceKlass* ik = classes->at(i); assert(ik->class_loader_data() != nullptr, "must be"); - if (ik->has_aot_initialized_mirror()) { - ik->initialize_with_aot_initialized_mirror(CHECK); + + bool do_init = ik->has_aot_initialized_mirror(); + if (do_init && early_only && !is_early_init_possible(ik)) { + // ik will be proactively initialized later when init_classes_for_loader() + // is called again with (early_only == false). + do_init = false; + } + + if (do_init) { + ik->initialize_with_aot_initialized_mirror(early_only, CHECK); } } } - HeapShared::init_classes_for_special_subgraph(class_loader, CHECK); + if (!early_only) { + HeapShared::init_classes_for_special_subgraph(class_loader, CHECK); + } } void AOTLinkedClassBulkLoader::replay_training_at_init(Array* classes, TRAPS) { diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp index 31fdac386fe..24ff61cea1e 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -56,7 +56,7 @@ class AOTLinkedClassBulkLoader : AllStatic { static void link_classes_impl(TRAPS); static void link_classes_in_table(Array* classes, TRAPS); static void init_non_javabase_classes_impl(TRAPS); - static void init_classes_for_loader(Handle class_loader, Array* classes, TRAPS); + static void init_classes_for_loader(Handle class_loader, Array* classes, bool early_only, TRAPS); static void replay_training_at_init(Array* classes, TRAPS) NOT_CDS_RETURN; #ifdef ASSERT @@ -73,8 +73,9 @@ public: static void init_javabase_classes(JavaThread* current) NOT_CDS_RETURN; static void init_non_javabase_classes(JavaThread* current) NOT_CDS_RETURN; static void exit_on_exception(JavaThread* current); - static void replay_training_at_init_for_preloaded_classes(TRAPS) NOT_CDS_RETURN; + + static bool is_initializing_classes_early() NOT_DEBUG({return false;}); }; #endif // SHARE_CDS_AOTLINKEDCLASSBULKLOADER_HPP diff --git a/src/hotspot/share/cds/aotMapLogger.cpp b/src/hotspot/share/cds/aotMapLogger.cpp index fa769aee1bf..98336ff9b1f 100644 --- a/src/hotspot/share/cds/aotMapLogger.cpp +++ b/src/hotspot/share/cds/aotMapLogger.cpp @@ -98,8 +98,8 @@ void AOTMapLogger::dumptime_log(ArchiveBuilder* builder, FileMapInfo* mapinfo, DumpRegion* rw_region = &builder->_rw_region; DumpRegion* ro_region = &builder->_ro_region; - dumptime_log_metaspace_region("rw region", rw_region, &builder->_rw_src_objs); - dumptime_log_metaspace_region("ro region", ro_region, &builder->_ro_src_objs); + dumptime_log_metaspace_region("rw region", rw_region, &builder->_rw_src_objs, &builder->_ro_src_objs); + dumptime_log_metaspace_region("ro region", ro_region, &builder->_rw_src_objs, &builder->_ro_src_objs); address bitmap_end = address(bitmap + bitmap_size_in_bytes); log_region_range("bitmap", address(bitmap), bitmap_end, nullptr); @@ -122,17 +122,6 @@ void AOTMapLogger::dumptime_log(ArchiveBuilder* builder, FileMapInfo* mapinfo, class AOTMapLogger::RuntimeGatherArchivedMetaspaceObjs : public UniqueMetaspaceClosure { GrowableArrayCHeap _objs; - static int compare_objs_by_addr(ArchivedObjInfo* a, ArchivedObjInfo* b) { - intx diff = a->_src_addr - b->_src_addr; - if (diff < 0) { - return -1; - } else if (diff == 0) { - return 0; - } else { - return 1; - } - } - public: GrowableArrayCHeap* objs() { return &_objs; } @@ -152,7 +141,7 @@ public: void finish() { UniqueMetaspaceClosure::finish(); - _objs.sort(compare_objs_by_addr); + _objs.sort(compare_by_address); } }; // AOTMapLogger::RuntimeGatherArchivedMetaspaceObjs @@ -203,24 +192,47 @@ void AOTMapLogger::runtime_log(FileMapInfo* mapinfo, GrowableArrayCHeapbase()); address region_top = address(region->top()); log_region_range(name, region_base, region_top, region_base + _buffer_to_requested_delta); if (log_is_enabled(Debug, aot, map)) { GrowableArrayCHeap objs; - for (int i = 0; i < src_objs->objs()->length(); i++) { - ArchiveBuilder::SourceObjInfo* src_info = src_objs->at(i); + // With -XX:+UseCompactObjectHeaders, it's possible for small objects (including some from + // ro_objs) to be allocated in the gaps in the RW region. + collect_metaspace_objs(&objs, region_base, region_top, rw_objs); + collect_metaspace_objs(&objs, region_base, region_top, ro_objs); + objs.sort(compare_by_address); + log_metaspace_objects_impl(address(region->base()), address(region->end()), &objs, 0, objs.length()); + } +} + +void AOTMapLogger::collect_metaspace_objs(GrowableArrayCHeap* objs, + address region_base, address region_top , + const ArchiveBuilder::SourceObjList* src_objs) { + for (int i = 0; i < src_objs->objs()->length(); i++) { + ArchiveBuilder::SourceObjInfo* src_info = src_objs->at(i); + address buf_addr = src_info->buffered_addr(); + if (region_base <= buf_addr && buf_addr < region_top) { ArchivedObjInfo info; info._src_addr = src_info->source_addr(); - info._buffered_addr = src_info->buffered_addr(); + info._buffered_addr = buf_addr; info._requested_addr = info._buffered_addr + _buffer_to_requested_delta; info._bytes = src_info->size_in_bytes(); info._type = src_info->type(); - objs.append(info); + objs->append(info); } + } +} - log_metaspace_objects_impl(address(region->base()), address(region->end()), &objs, 0, objs.length()); +int AOTMapLogger::compare_by_address(ArchivedObjInfo* a, ArchivedObjInfo* b) { + if (a->_buffered_addr < b->_buffered_addr) { + return -1; + } else if (a->_buffered_addr > b->_buffered_addr) { + return 1; + } else { + return 0; } } diff --git a/src/hotspot/share/cds/aotMapLogger.hpp b/src/hotspot/share/cds/aotMapLogger.hpp index f495ed97f40..0a89f1e5012 100644 --- a/src/hotspot/share/cds/aotMapLogger.hpp +++ b/src/hotspot/share/cds/aotMapLogger.hpp @@ -127,7 +127,12 @@ private: static void runtime_log(FileMapInfo* mapinfo, GrowableArrayCHeap* objs); static void runtime_log_metaspace_regions(FileMapInfo* mapinfo, GrowableArrayCHeap* objs); static void dumptime_log_metaspace_region(const char* name, DumpRegion* region, - const ArchiveBuilder::SourceObjList* src_objs); + const ArchiveBuilder::SourceObjList* rw_objs, + const ArchiveBuilder::SourceObjList* ro_objs); + static void collect_metaspace_objs(GrowableArrayCHeap* objs, + address region_base, address region_top , + const ArchiveBuilder::SourceObjList* src_objs); + static int compare_by_address(ArchivedObjInfo* a, ArchivedObjInfo* b); // Common code for dumptime/runtime static void log_file_header(FileMapInfo* mapinfo); diff --git a/src/hotspot/share/cds/aotMappedHeapWriter.cpp b/src/hotspot/share/cds/aotMappedHeapWriter.cpp index 64c0e3c40e8..3456c845938 100644 --- a/src/hotspot/share/cds/aotMappedHeapWriter.cpp +++ b/src/hotspot/share/cds/aotMappedHeapWriter.cpp @@ -64,6 +64,11 @@ HeapRootSegments AOTMappedHeapWriter::_heap_root_segments; address AOTMappedHeapWriter::_requested_bottom; address AOTMappedHeapWriter::_requested_top; +static size_t _num_strings = 0; +static size_t _string_bytes = 0; +static size_t _num_packages = 0; +static size_t _num_protection_domains = 0; + GrowableArrayCHeap* AOTMappedHeapWriter::_native_pointers; GrowableArrayCHeap* AOTMappedHeapWriter::_source_objs; GrowableArrayCHeap* AOTMappedHeapWriter::_source_objs_order; @@ -71,8 +76,6 @@ GrowableArrayCHeap* AOTMappedH AOTMappedHeapWriter::BufferOffsetToSourceObjectTable* AOTMappedHeapWriter::_buffer_offset_to_source_obj_table = nullptr; -DumpedInternedStrings *AOTMappedHeapWriter::_dumped_interned_strings = nullptr; - typedef HashTable< size_t, // offset of a filler from AOTMappedHeapWriter::buffer_bottom() size_t, // size of this filler (in bytes) @@ -87,7 +90,6 @@ void AOTMappedHeapWriter::init() { Universe::heap()->collect(GCCause::_java_lang_system_gc); _buffer_offset_to_source_obj_table = new (mtClassShared) BufferOffsetToSourceObjectTable(/*size (prime)*/36137, /*max size*/1 * M); - _dumped_interned_strings = new (mtClass)DumpedInternedStrings(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE); _fillers = new (mtClassShared) FillersTable(); _requested_bottom = nullptr; _requested_top = nullptr; @@ -141,9 +143,6 @@ int AOTMappedHeapWriter::narrow_oop_shift() { void AOTMappedHeapWriter::delete_tables_with_raw_oops() { delete _source_objs; _source_objs = nullptr; - - delete _dumped_interned_strings; - _dumped_interned_strings = nullptr; } void AOTMappedHeapWriter::add_source_obj(oop src_obj) { @@ -181,25 +180,6 @@ bool AOTMappedHeapWriter::is_too_large_to_archive(size_t size) { } } -// Keep track of the contents of the archived interned string table. This table -// is used only by CDSHeapVerifier. -void AOTMappedHeapWriter::add_to_dumped_interned_strings(oop string) { - assert_at_safepoint(); // DumpedInternedStrings uses raw oops - assert(!is_string_too_large_to_archive(string), "must be"); - bool created; - _dumped_interned_strings->put_if_absent(string, true, &created); - if (created) { - // Prevent string deduplication from changing the value field to - // something not in the archive. - java_lang_String::set_deduplication_forbidden(string); - _dumped_interned_strings->maybe_grow(); - } -} - -bool AOTMappedHeapWriter::is_dumped_interned_string(oop o) { - return _dumped_interned_strings->get(o) != nullptr; -} - // Various lookup functions between source_obj, buffered_obj and requested_obj bool AOTMappedHeapWriter::is_in_requested_range(oop o) { assert(_requested_bottom != nullptr, "do not call before _requested_bottom is initialized"); @@ -430,6 +410,7 @@ void AOTMappedHeapWriter::copy_source_objs_to_buffer(GrowableArrayCHeapset_buffer_offset(buffer_offset); + assert(buffer_offset <= 0x7fffffff, "sanity"); OopHandle handle(Universe::vm_global(), src_obj); _buffer_offset_to_source_obj_table->put_when_absent(buffer_offset, handle); @@ -442,6 +423,9 @@ void AOTMappedHeapWriter::copy_source_objs_to_buffer(GrowableArrayCHeaplength() + 1, roots->length(), _num_native_ptrs); + log_info(aot)(" strings = %8zu (%zu bytes)", _num_strings, _string_bytes); + log_info(aot)(" packages = %8zu", _num_packages); + log_info(aot)(" protection domains = %8zu", _num_protection_domains); } size_t AOTMappedHeapWriter::filler_array_byte_size(int length) { @@ -530,7 +514,25 @@ void update_buffered_object_field(address buffered_obj, int field_offset, T valu *field_addr = value; } +void AOTMappedHeapWriter::update_stats(oop src_obj) { + if (java_lang_String::is_instance(src_obj)) { + _num_strings ++; + _string_bytes += src_obj->size() * HeapWordSize; + _string_bytes += java_lang_String::value(src_obj)->size() * HeapWordSize; + } else { + Klass* k = src_obj->klass(); + Symbol* name = k->name(); + if (name->equals("java/lang/NamedPackage") || name->equals("java/lang/Package")) { + _num_packages ++; + } else if (name->equals("java/security/ProtectionDomain")) { + _num_protection_domains ++; + } + } +} + size_t AOTMappedHeapWriter::copy_one_source_obj_to_buffer(oop src_obj) { + update_stats(src_obj); + assert(!is_too_large_to_archive(src_obj), "already checked"); size_t byte_size = src_obj->size() * HeapWordSize; assert(byte_size > 0, "no zero-size objects"); @@ -896,8 +898,14 @@ void AOTMappedHeapWriter::compute_ptrmap(AOTMappedHeapInfo* heap_info) { native_ptr = RegeneratedClasses::get_regenerated_object(native_ptr); } - guarantee(ArchiveBuilder::current()->has_been_archived((address)native_ptr), - "Metadata %p should have been archived", native_ptr); + if (!ArchiveBuilder::current()->has_been_archived((address)native_ptr)) { + ResourceMark rm; + LogStreamHandle(Error, aot) log; + log.print("Marking native pointer for oop %p (type = %s, offset = %d)", + cast_from_oop(src_obj), src_obj->klass()->external_name(), field_offset); + src_obj->print_on(&log); + fatal("Metadata %p should have been archived", native_ptr); + } address buffered_native_ptr = ArchiveBuilder::current()->get_buffered_addr((address)native_ptr); address requested_native_ptr = ArchiveBuilder::current()->to_requested(buffered_native_ptr); diff --git a/src/hotspot/share/cds/aotMappedHeapWriter.hpp b/src/hotspot/share/cds/aotMappedHeapWriter.hpp index 7481e7922a0..2420e68d9fe 100644 --- a/src/hotspot/share/cds/aotMappedHeapWriter.hpp +++ b/src/hotspot/share/cds/aotMappedHeapWriter.hpp @@ -40,20 +40,6 @@ class MemRegion; #if INCLUDE_CDS_JAVA_HEAP -class DumpedInternedStrings : - public ResizeableHashTable -{ -public: - DumpedInternedStrings(unsigned size, unsigned max_size) : - ResizeableHashTable(size, max_size) {} -}; - class AOTMappedHeapWriter : AllStatic { friend class HeapShared; friend class AOTMappedHeapLoader; @@ -131,7 +117,6 @@ private: static GrowableArrayCHeap* _native_pointers; static GrowableArrayCHeap* _source_objs; - static DumpedInternedStrings *_dumped_interned_strings; // We sort _source_objs_order to minimize the number of bits in ptrmap and oopmap. // See comments near the body of AOTMappedHeapWriter::compare_objs_by_oop_fields(). @@ -190,6 +175,7 @@ private: static void copy_roots_to_buffer(GrowableArrayCHeap* roots); static void copy_source_objs_to_buffer(GrowableArrayCHeap* roots); static size_t copy_one_source_obj_to_buffer(oop src_obj); + static void update_stats(oop src_obj); static void maybe_fill_gc_region_gap(size_t required_byte_size); static size_t filler_array_byte_size(int length); @@ -227,8 +213,6 @@ public: static bool is_too_large_to_archive(size_t size); static bool is_too_large_to_archive(oop obj); static bool is_string_too_large_to_archive(oop string); - static bool is_dumped_interned_string(oop o); - static void add_to_dumped_interned_strings(oop string); static void write(GrowableArrayCHeap*, AOTMappedHeapInfo* heap_info); static address requested_address(); // requested address of the lowest achived heap object static size_t get_filler_size_at(address buffered_addr); diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index b75d7628aa9..76c37194882 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -1187,8 +1187,8 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS CDSConfig::enable_dumping_aot_code(); { builder.start_ac_region(); - // Write the contents to AOT code region and close AOTCodeCache before packing the region - AOTCodeCache::close(); + // Write the contents to AOT code region before packing the region + AOTCodeCache::dump(); builder.end_ac_region(); } CDSConfig::disable_dumping_aot_code(); @@ -1812,15 +1812,10 @@ MapArchiveResult AOTMetaspace::map_archives(FileMapInfo* static_mapinfo, FileMap } else { FileMapRegion* r = static_mapinfo->region_at(AOTMetaspace::hp); if (r->used() > 0) { - if (static_mapinfo->object_streaming_mode()) { - AOTMetaspace::report_loading_error("Cannot use CDS heap data."); - } else { - if (!UseCompressedOops && !AOTMappedHeapLoader::can_map()) { - AOTMetaspace::report_loading_error("Cannot use CDS heap data. Selected GC not compatible -XX:-UseCompressedOops"); - } else { - AOTMetaspace::report_loading_error("Cannot use CDS heap data. UseEpsilonGC, UseG1GC, UseSerialGC, UseParallelGC, or UseShenandoahGC are required."); - } - } + AOTMetaspace::report_loading_error("Cannot use CDS heap data."); + } + if (!CDSConfig::is_dumping_static_archive()) { + CDSConfig::stop_using_full_module_graph("No CDS heap data"); } } } diff --git a/src/hotspot/share/cds/aotReferenceObjSupport.cpp b/src/hotspot/share/cds/aotReferenceObjSupport.cpp index 0c27c8ce5f0..2d5fc8c7f21 100644 --- a/src/hotspot/share/cds/aotReferenceObjSupport.cpp +++ b/src/hotspot/share/cds/aotReferenceObjSupport.cpp @@ -96,7 +96,7 @@ class KeepAliveObjectsTable : public HashTable {}; + HeapShared::oop_address_hash> {}; static KeepAliveObjectsTable* _keep_alive_objs_table; static OopHandle _keep_alive_objs_array; diff --git a/src/hotspot/share/cds/aotStreamedHeapWriter.cpp b/src/hotspot/share/cds/aotStreamedHeapWriter.cpp index f52532b2f2a..ad363f21fdb 100644 --- a/src/hotspot/share/cds/aotStreamedHeapWriter.cpp +++ b/src/hotspot/share/cds/aotStreamedHeapWriter.cpp @@ -242,20 +242,6 @@ void AOTStreamedHeapWriter::copy_roots_max_dfs_to_buffer(int roots_length) { } } -static bool is_interned_string(oop obj) { - if (!java_lang_String::is_instance(obj)) { - return false; - } - - ResourceMark rm; - int len; - jchar* name = java_lang_String::as_unicode_string_or_null(obj, len); - if (name == nullptr) { - fatal("Insufficient memory for dumping"); - } - return StringTable::lookup(name, len) == obj; -} - static BitMap::idx_t bit_idx_for_buffer_offset(size_t buffer_offset) { if (UseCompressedOops) { return BitMap::idx_t(buffer_offset / sizeof(narrowOop)); @@ -264,10 +250,6 @@ static BitMap::idx_t bit_idx_for_buffer_offset(size_t buffer_offset) { } } -bool AOTStreamedHeapWriter::is_dumped_interned_string(oop obj) { - return is_interned_string(obj) && HeapShared::get_cached_oop_info(obj) != nullptr; -} - void AOTStreamedHeapWriter::copy_source_objs_to_buffer(GrowableArrayCHeap* roots) { for (int i = 0; i < _source_objs->length(); i++) { oop src_obj = _source_objs->at(i); @@ -325,7 +307,7 @@ size_t AOTStreamedHeapWriter::copy_one_source_obj_to_buffer(oop src_obj) { ensure_buffer_space(new_used); - if (is_interned_string(src_obj)) { + if (HeapShared::is_interned_string(src_obj)) { java_lang_String::hash_code(src_obj); // Sets the hash code field(s) java_lang_String::set_deduplication_forbidden(src_obj); // Allows faster interning at runtime assert(java_lang_String::hash_is_set(src_obj), "hash must be set"); @@ -402,7 +384,7 @@ void AOTStreamedHeapWriter::update_header_for_buffered_addr(address buffered_add mw = mw.copy_set_hash(src_hash); } - if (is_interned_string(src_obj)) { + if (HeapShared::is_interned_string(src_obj)) { // Mark the mark word of interned string so the loader knows to link these to // the string table at runtime. mw = mw.set_marked(); diff --git a/src/hotspot/share/cds/aotStreamedHeapWriter.hpp b/src/hotspot/share/cds/aotStreamedHeapWriter.hpp index ab5aec0327b..c3cc9f2c092 100644 --- a/src/hotspot/share/cds/aotStreamedHeapWriter.hpp +++ b/src/hotspot/share/cds/aotStreamedHeapWriter.hpp @@ -148,8 +148,6 @@ public: return size_t(buffered_addr) - size_t(buffer_bottom()); } - static bool is_dumped_interned_string(oop obj); - static size_t source_obj_to_buffered_offset(oop src_obj); static address source_obj_to_buffered_addr(oop src_obj); diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index 0ea5d6c6ecb..b2d6600e44f 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -627,6 +627,7 @@ void ArchiveBuilder::dump_ro_metadata() { start_dump_region(&_ro_region); make_shallow_copies(&_ro_region, &_ro_src_objs); RegeneratedClasses::record_regenerated_objects(); + DumpRegion::report_gaps(&_alloc_stats); } void ArchiveBuilder::make_shallow_copies(DumpRegion *dump_region, @@ -639,33 +640,10 @@ void ArchiveBuilder::make_shallow_copies(DumpRegion *dump_region, void ArchiveBuilder::make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* src_info) { address src = src_info->source_addr(); - int bytes = src_info->size_in_bytes(); // word-aligned - size_t alignment = SharedSpaceObjectAlignment; // alignment for the dest pointer + int bytes = src_info->size_in_bytes(); + char* dest = dump_region->allocate_metaspace_obj(bytes, src, src_info->type(), + src_info->read_only(), &_alloc_stats); - char* oldtop = dump_region->top(); - if (src_info->type() == MetaspaceClosureType::ClassType) { - // Allocate space for a pointer directly in front of the future InstanceKlass, so - // we can do a quick lookup from InstanceKlass* -> RunTimeClassInfo* - // without building another hashtable. See RunTimeClassInfo::get_for() - // in systemDictionaryShared.cpp. - Klass* klass = (Klass*)src; - if (klass->is_instance_klass()) { - SystemDictionaryShared::validate_before_archiving(InstanceKlass::cast(klass)); - dump_region->allocate(sizeof(address)); - } -#ifdef _LP64 - // More strict alignments needed for UseCompressedClassPointers - if (UseCompressedClassPointers) { - alignment = nth_bit(ArchiveBuilder::precomputed_narrow_klass_shift()); - } -#endif - } else if (src_info->type() == MetaspaceClosureType::SymbolType) { - // Symbols may be allocated by using AllocateHeap, so their sizes - // may be less than size_in_bytes() indicates. - bytes = ((Symbol*)src)->byte_size(); - } - - char* dest = dump_region->allocate(bytes, alignment); memcpy(dest, src, bytes); // Update the hash of buffered sorted symbols for static dump so that the symbols have deterministic contents @@ -692,11 +670,6 @@ void ArchiveBuilder::make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* s log_trace(aot)("Copy: " PTR_FORMAT " ==> " PTR_FORMAT " %d", p2i(src), p2i(dest), bytes); src_info->set_buffered_addr((address)dest); - - char* newtop = dump_region->top(); - _alloc_stats.record(src_info->type(), int(newtop - oldtop), src_info->read_only()); - - DEBUG_ONLY(_alloc_stats.verify((int)dump_region->used(), src_info->read_only())); } // This is used by code that hand-assembles data structures, such as the LambdaProxyClassKey, that are diff --git a/src/hotspot/share/cds/archiveUtils.cpp b/src/hotspot/share/cds/archiveUtils.cpp index ea9bde8eb8d..f79b1e134e8 100644 --- a/src/hotspot/share/cds/archiveUtils.cpp +++ b/src/hotspot/share/cds/archiveUtils.cpp @@ -30,6 +30,7 @@ #include "cds/cdsConfig.hpp" #include "cds/classListParser.hpp" #include "cds/classListWriter.hpp" +#include "cds/dumpAllocStats.hpp" #include "cds/dynamicArchive.hpp" #include "cds/filemap.hpp" #include "cds/heapShared.hpp" @@ -46,6 +47,7 @@ #include "utilities/debug.hpp" #include "utilities/formatBuffer.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/rbTree.inline.hpp" #include "utilities/spinYield.hpp" CHeapBitMap* ArchivePtrMarker::_ptrmap = nullptr; @@ -116,13 +118,17 @@ void ArchivePtrMarker::mark_pointer(address* ptr_loc) { if (ptr_base() <= ptr_loc && ptr_loc < ptr_end()) { address value = *ptr_loc; - // We don't want any pointer that points to very bottom of the archive, otherwise when - // AOTMetaspace::default_base_address()==0, we can't distinguish between a pointer - // to nothing (null) vs a pointer to an objects that happens to be at the very bottom - // of the archive. - assert(value != (address)ptr_base(), "don't point to the bottom of the archive"); - if (value != nullptr) { + // We don't want any pointer that points to very bottom of the AOT metaspace, otherwise + // when AOTMetaspace::default_base_address()==0, we can't distinguish between a pointer + // to nothing (null) vs a pointer to an objects that happens to be at the very bottom + // of the AOT metaspace. + // + // This should never happen because the protection zone prevents any valid objects from + // being allocated at the bottom of the AOT metaspace. + assert(AOTMetaspace::protection_zone_size() > 0, "must be"); + assert(ArchiveBuilder::current()->any_to_offset(value) > 0, "cannot point to bottom of AOT metaspace"); + assert(uintx(ptr_loc) % sizeof(intptr_t) == 0, "pointers must be stored in aligned addresses"); size_t idx = ptr_loc - ptr_base(); if (_ptrmap->size() <= idx) { @@ -130,7 +136,6 @@ void ArchivePtrMarker::mark_pointer(address* ptr_loc) { } assert(idx < _ptrmap->size(), "must be"); _ptrmap->set_bit(idx); - //tty->print_cr("Marking pointer [" PTR_FORMAT "] -> " PTR_FORMAT " @ %5zu", p2i(ptr_loc), p2i(*ptr_loc), idx); } } } @@ -144,7 +149,6 @@ void ArchivePtrMarker::clear_pointer(address* ptr_loc) { size_t idx = ptr_loc - ptr_base(); assert(idx < _ptrmap->size(), "cannot clear pointers that have not been marked"); _ptrmap->clear_bit(idx); - //tty->print_cr("Clearing pointer [" PTR_FORMAT "] -> " PTR_FORMAT " @ %5zu", p2i(ptr_loc), p2i(*ptr_loc), idx); } class ArchivePtrBitmapCleaner: public BitMapClosure { @@ -249,16 +253,179 @@ void DumpRegion::commit_to(char* newtop) { which, commit, _vs->actual_committed_size(), _vs->high()); } +// Basic allocation. Any alignment gaps will be wasted. char* DumpRegion::allocate(size_t num_bytes, size_t alignment) { // Always align to at least minimum alignment alignment = MAX2(SharedSpaceObjectAlignment, alignment); char* p = (char*)align_up(_top, alignment); - char* newtop = p + align_up(num_bytes, (size_t)SharedSpaceObjectAlignment); + char* newtop = p + align_up(num_bytes, SharedSpaceObjectAlignment); expand_top_to(newtop); memset(p, 0, newtop - p); return p; } +class DumpRegion::AllocGap { + size_t _gap_bytes; // size of this gap in bytes + char* _gap_bottom; // must be SharedSpaceObjectAlignment aligned +public: + size_t gap_bytes() const { return _gap_bytes; } + char* gap_bottom() const { return _gap_bottom; } + + AllocGap(size_t bytes, char* bottom) : _gap_bytes(bytes), _gap_bottom(bottom) { + precond(is_aligned(gap_bytes(), SharedSpaceObjectAlignment)); + precond(is_aligned(gap_bottom(), SharedSpaceObjectAlignment)); + } +}; + +struct DumpRegion::AllocGapCmp { + static RBTreeOrdering cmp(AllocGap a, AllocGap b) { + RBTreeOrdering order = rbtree_primitive_cmp(a.gap_bytes(), b.gap_bytes()); + if (order == RBTreeOrdering::EQ) { + order = rbtree_primitive_cmp(a.gap_bottom(), b.gap_bottom()); + } + return order; + } +}; + +struct Empty {}; +using AllocGapNode = RBNode; + +class DumpRegion::AllocGapTree : public RBTreeCHeap { +public: + size_t add_gap(char* gap_bottom, char* gap_top) { + precond(gap_bottom < gap_top); + size_t gap_bytes = pointer_delta(gap_top, gap_bottom, 1); + precond(gap_bytes > 0); + + _total_gap_bytes += gap_bytes; + + AllocGap gap(gap_bytes, gap_bottom); // constructor checks alignment + AllocGapNode* node = allocate_node(gap, Empty{}); + insert(gap, node); + + log_trace(aot, alloc)("adding a gap of %zu bytes @ %p (total = %zu) in %zu blocks", gap_bytes, gap_bottom, _total_gap_bytes, size()); + return gap_bytes; + } + + char* allocate_from_gap(size_t num_bytes) { + // The gaps are sorted in ascending order of their sizes. When two gaps have the same + // size, the one with a lower gap_bottom comes first. + // + // Find the first gap that's big enough, with the lowest gap_bottom. + AllocGap target(num_bytes, nullptr); + AllocGapNode* node = closest_ge(target); + if (node == nullptr) { + return nullptr; // Didn't find any usable gap. + } + + size_t gap_bytes = node->key().gap_bytes(); + char* gap_bottom = node->key().gap_bottom(); + char* result = gap_bottom; + precond(is_aligned(result, SharedSpaceObjectAlignment)); + + remove(node); + + precond(_total_gap_bytes >= num_bytes); + _total_gap_bytes -= num_bytes; + _total_gap_bytes_used += num_bytes; + _total_gap_allocs++; + DEBUG_ONLY(node = nullptr); // Don't use it anymore! + + precond(gap_bytes >= num_bytes); + if (gap_bytes > num_bytes) { + gap_bytes -= num_bytes; + gap_bottom += num_bytes; + + AllocGap gap(gap_bytes, gap_bottom); // constructor checks alignment + AllocGapNode* new_node = allocate_node(gap, Empty{}); + insert(gap, new_node); + } + log_trace(aot, alloc)("%zu bytes @ %p in a gap of %zu bytes (used gaps %zu times, remain gap = %zu bytes in %zu blocks)", + num_bytes, result, gap_bytes, _total_gap_allocs, _total_gap_bytes, size()); + return result; + } +}; + +size_t DumpRegion::_total_gap_bytes = 0; +size_t DumpRegion::_total_gap_bytes_used = 0; +size_t DumpRegion::_total_gap_allocs = 0; +DumpRegion::AllocGapTree DumpRegion::_gap_tree; + +// Alignment gaps happen only for the RW space. Collect the gaps into the _gap_tree so they can be +// used for future small object allocation. +char* DumpRegion::allocate_metaspace_obj(size_t num_bytes, address src, MetaspaceClosureType type, bool read_only, DumpAllocStats* stats) { + num_bytes = align_up(num_bytes, SharedSpaceObjectAlignment); + size_t alignment = SharedSpaceObjectAlignment; // alignment for the dest pointer + bool is_class = (type == MetaspaceClosureType::ClassType); + bool is_instance_class = is_class && ((Klass*)src)->is_instance_klass(); + +#ifdef _LP64 + // More strict alignments needed for UseCompressedClassPointers + if (is_class && UseCompressedClassPointers) { + size_t klass_alignment = checked_cast(nth_bit(ArchiveBuilder::precomputed_narrow_klass_shift())); + alignment = MAX2(alignment, klass_alignment); + precond(is_aligned(alignment, SharedSpaceObjectAlignment)); + } +#endif + + if (alignment == SharedSpaceObjectAlignment && type != MetaspaceClosureType::SymbolType) { + // The addresses of Symbols must be in the same order as they are in ArchiveBuilder::SourceObjList. + // If we put them in gaps, their order will change. + // + // We have enough small objects that all gaps are usually filled. + char* p = _gap_tree.allocate_from_gap(num_bytes); + if (p != nullptr) { + // Already memset to 0 when adding the gap + stats->record(type, checked_cast(num_bytes), /*read_only=*/false); // all gaps are from RW space (for classes) + return p; + } + } + + // Reserve space for a pointer directly in front of the buffered InstanceKlass, so + // we can do a quick lookup from InstanceKlass* -> RunTimeClassInfo* + // without building another hashtable. See RunTimeClassInfo::get_for() + // in systemDictionaryShared.cpp. + const size_t RuntimeClassInfoPtrSize = is_instance_class ? sizeof(address) : 0; + + if (is_class && !is_aligned(top() + RuntimeClassInfoPtrSize, alignment)) { + // We need to add a gap to align the buffered Klass. Save the gap for future small allocations. + assert(read_only == false, "only gaps in RW region are reusable"); + char* gap_bottom = top(); + char* gap_top = align_up(gap_bottom + RuntimeClassInfoPtrSize, alignment) - RuntimeClassInfoPtrSize; + size_t gap_bytes = _gap_tree.add_gap(gap_bottom, gap_top); + allocate(gap_bytes); + } + + char* oldtop = top(); + if (is_instance_class) { + SystemDictionaryShared::validate_before_archiving((InstanceKlass*)src); + allocate(RuntimeClassInfoPtrSize); + } + + precond(is_aligned(top(), alignment)); + char* result = allocate(num_bytes); + log_trace(aot, alloc)("%zu bytes @ %p", num_bytes, result); + stats->record(type, pointer_delta_as_int(top(), oldtop), read_only); // includes RuntimeClassInfoPtrSize for classes + + return result; +} + +// Usually we have no gaps left. +void DumpRegion::report_gaps(DumpAllocStats* stats) { + _gap_tree.visit_in_order([&](const AllocGapNode* node) { + stats->record_gap(checked_cast(node->key().gap_bytes())); + return true; + }); + if (_gap_tree.size() > 0) { + log_warning(aot)("Unexpected %zu gaps (%zu bytes) for Klass alignment", + _gap_tree.size(), _total_gap_bytes); + } + if (_total_gap_allocs > 0) { + log_info(aot)("Allocated %zu objects of %zu bytes in gaps (remain = %zu bytes)", + _total_gap_allocs, _total_gap_bytes_used, _total_gap_bytes); + } +} + void DumpRegion::append_intptr_t(intptr_t n, bool need_to_mark) { assert(is_aligned(_top, sizeof(intptr_t)), "bad alignment"); intptr_t *p = (intptr_t*)_top; diff --git a/src/hotspot/share/cds/archiveUtils.hpp b/src/hotspot/share/cds/archiveUtils.hpp index e5d1efa5eab..84ad8e6bdf3 100644 --- a/src/hotspot/share/cds/archiveUtils.hpp +++ b/src/hotspot/share/cds/archiveUtils.hpp @@ -28,7 +28,9 @@ #include "cds/cds_globals.hpp" #include "cds/serializeClosure.hpp" #include "logging/log.hpp" +#include "memory/allocation.hpp" #include "memory/metaspace.hpp" +#include "memory/metaspaceClosureType.hpp" #include "memory/virtualspace.hpp" #include "runtime/nonJavaThread.hpp" #include "runtime/semaphore.hpp" @@ -37,6 +39,7 @@ #include "utilities/macros.hpp" class BootstrapInfo; +class DumpAllocStats; class ReservedSpace; class VirtualSpace; @@ -159,6 +162,18 @@ private: void commit_to(char* newtop); +public: + // Allocation gaps (due to Klass alignment) + class AllocGapTree; + class AllocGap; + struct AllocGapCmp; + +private: + static AllocGapTree _gap_tree; + static size_t _total_gap_bytes; + static size_t _total_gap_bytes_used; + static size_t _total_gap_allocs; + public: DumpRegion(const char* name) : _name(name), _base(nullptr), _top(nullptr), _end(nullptr), @@ -167,6 +182,7 @@ public: char* expand_top_to(char* newtop); char* allocate(size_t num_bytes, size_t alignment = 0); + char* allocate_metaspace_obj(size_t num_bytes, address src, MetaspaceClosureType type, bool read_only, DumpAllocStats* stats); void append_intptr_t(intptr_t n, bool need_to_mark = false) NOT_CDS_RETURN; @@ -191,6 +207,8 @@ public: bool contains(char* p) { return base() <= p && p < top(); } + + static void report_gaps(DumpAllocStats* stats); }; // Closure for serializing initialization data out to a data area to be diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index f4ef3c66f7a..9d191bf0121 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -108,6 +108,8 @@ void CDSConfig::ergo_initialize() { } AOTMapLogger::ergo_initialize(); + + setup_compiler_args(); } const char* CDSConfig::default_archive_path() { @@ -635,8 +637,6 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla FLAG_SET_ERGO_IF_DEFAULT(AOTClassLinking, true); } - setup_compiler_args(); - if (AOTClassLinking) { // If AOTClassLinking is specified, enable all AOT optimizations by default. FLAG_SET_ERGO_IF_DEFAULT(AOTInvokeDynamicLinking, true); @@ -972,17 +972,27 @@ bool CDSConfig::is_loading_heap() { } bool CDSConfig::is_dumping_klass_subgraphs() { - if (is_dumping_classic_static_archive() || is_dumping_final_static_archive()) { + if (is_dumping_aot_linked_classes()) { // KlassSubGraphs (see heapShared.cpp) is a legacy mechanism for archiving oops. It // has been superceded by AOT class linking. This feature is used only when // AOT class linking is disabled. - // - // KlassSubGraphs are disabled in the preimage static archive, which contains a very - // limited set of oops. - return is_dumping_heap() && !is_dumping_aot_linked_classes(); - } else { return false; } + + if (is_dumping_preimage_static_archive()) { + // KlassSubGraphs are disabled in the preimage static archive, which contains a very + // limited set of oops. + return false; + } + + if (!is_dumping_full_module_graph()) { + // KlassSubGraphs cannot be partially disabled. Since some of the KlassSubGraphs + // are used for (legacy support) of the archived full module graph, if + // is_dumping_full_module_graph() is calse, we must disable all KlassSubGraphs. + return false; + } + + return is_dumping_heap(); } bool CDSConfig::is_using_klass_subgraphs() { diff --git a/src/hotspot/share/cds/cdsHeapVerifier.hpp b/src/hotspot/share/cds/cdsHeapVerifier.hpp index 7f1bdb1d249..f8e090801bb 100644 --- a/src/hotspot/share/cds/cdsHeapVerifier.hpp +++ b/src/hotspot/share/cds/cdsHeapVerifier.hpp @@ -53,7 +53,7 @@ class CDSHeapVerifier : public KlassClosure { 15889, // prime number AnyObj::C_HEAP, mtClassShared, - HeapShared::oop_hash> _table; + HeapShared::oop_address_hash> _table; GrowableArray _exclusions; GrowableArray _shared_secret_accessors; diff --git a/src/hotspot/share/cds/dumpAllocStats.cpp b/src/hotspot/share/cds/dumpAllocStats.cpp index 5f324566103..ddd4bac6086 100644 --- a/src/hotspot/share/cds/dumpAllocStats.cpp +++ b/src/hotspot/share/cds/dumpAllocStats.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -129,15 +129,3 @@ void DumpAllocStats::print_stats(int ro_all, int rw_all) { _bytes [RW][MethodTrainingDataType]); } - -#ifdef ASSERT -void DumpAllocStats::verify(int expected_byte_size, bool read_only) const { - int bytes = 0; - const int what = (int)(read_only ? RO : RW); - for (int type = 0; type < int(_number_of_types); type ++) { - bytes += _bytes[what][type]; - } - assert(bytes == expected_byte_size, "counter mismatch (%s: %d vs %d)", - (read_only ? "RO" : "RW"), bytes, expected_byte_size); -} -#endif // ASSERT diff --git a/src/hotspot/share/cds/dumpAllocStats.hpp b/src/hotspot/share/cds/dumpAllocStats.hpp index 4553f0f6a01..4daef9195a6 100644 --- a/src/hotspot/share/cds/dumpAllocStats.hpp +++ b/src/hotspot/share/cds/dumpAllocStats.hpp @@ -41,6 +41,7 @@ public: f(StringHashentry) \ f(StringBucket) \ f(CppVTables) \ + f(Gap) \ f(Other) #define DUMPED_TYPE_DECLARE(name) name ## Type, @@ -111,12 +112,19 @@ public: _bytes [which][t] += byte_size; } + void record_gap(int byte_size) { + _counts[RW][GapType] += 1; + _bytes [RW][GapType] += byte_size; + } + void record_other_type(int byte_size, bool read_only) { int which = (read_only) ? RO : RW; + _counts[which][OtherType] += 1; _bytes [which][OtherType] += byte_size; } void record_cpp_vtables(int byte_size) { + _counts[RW][CppVTablesType] += 1; _bytes[RW][CppVTablesType] += byte_size; } @@ -145,9 +153,6 @@ public: } void print_stats(int ro_all, int rw_all); - - DEBUG_ONLY(void verify(int expected_byte_size, bool read_only) const); - }; #endif // SHARE_CDS_DUMPALLOCSTATS_HPP diff --git a/src/hotspot/share/cds/dumpTimeClassInfo.hpp b/src/hotspot/share/cds/dumpTimeClassInfo.hpp index c2f83b22337..d2de8148bea 100644 --- a/src/hotspot/share/cds/dumpTimeClassInfo.hpp +++ b/src/hotspot/share/cds/dumpTimeClassInfo.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -41,7 +41,6 @@ class Symbol; class DumpTimeClassInfo: public CHeapObj { bool _excluded; bool _is_aot_tooling_class; - bool _is_early_klass; bool _has_checked_exclusion; class DTLoaderConstraint { @@ -143,7 +142,6 @@ public: _clsfile_crc32 = -1; _excluded = false; _is_aot_tooling_class = false; - _is_early_klass = JvmtiExport::is_early_phase(); _verifier_constraints = nullptr; _verifier_constraint_flags = nullptr; _loader_constraints = nullptr; @@ -219,11 +217,6 @@ public: _is_aot_tooling_class = true; } - // Was this class loaded while JvmtiExport::is_early_phase()==true - bool is_early_klass() { - return _is_early_klass; - } - // simple accessors void set_excluded() { _excluded = true; } bool has_checked_exclusion() const { return _has_checked_exclusion; } diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index 91fbce701e5..a07f13cefca 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -1535,10 +1535,34 @@ bool FileMapInfo::can_use_heap_region() { if (!has_heap_region()) { return false; } - if (!object_streaming_mode() && !Universe::heap()->can_load_archived_objects() && !UseG1GC) { - // Incompatible object format + + if (!object_streaming_mode() && !AOTMappedHeapLoader::can_use()) { + // Currently this happens only when using ZGC with an AOT cache generated with -XX:-AOTStreamableObjects + AOTMetaspace::report_loading_error("CDS heap data cannot be used by the selected GC. " + "Please choose a different GC or rebuild AOT cache " + "with -XX:+AOTStreamableObjects"); return false; } + + if (CDSConfig::is_using_aot_linked_classes()) { + assert(!JvmtiExport::should_post_class_file_load_hook(), "already checked"); + assert(CDSConfig::is_using_full_module_graph(), "already checked"); + } else { + if (JvmtiExport::should_post_class_file_load_hook()) { + AOTMetaspace::report_loading_error("CDS heap data is disabled because JVMTI ClassFileLoadHook is in use."); + return false; + } + if (!CDSConfig::is_using_full_module_graph()) { + if (CDSConfig::is_dumping_final_static_archive()) { + // We are loading the preimage static archive, which has no KlassSubGraphs. + // See CDSConfig::is_dumping_klass_subgraphs() + } else { + AOTMetaspace::report_loading_error("CDS heap data is disabled because archived full module graph is not used."); + return false; + } + } + } + if (JvmtiExport::should_post_class_file_load_hook() && JvmtiExport::has_early_class_hook_env()) { ShouldNotReachHere(); // CDS should have been disabled. // The archived objects are mapped at JVM start-up, but we don't know if diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 0c0f70eac0a..708a19e9a7d 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -131,17 +131,14 @@ static ArchivableStaticFieldInfo archive_subgraph_entry_fields[] = { {"java/lang/module/Configuration", "EMPTY_CONFIGURATION"}, {"jdk/internal/math/FDBigInteger", "archivedCaches"}, -#ifndef PRODUCT - {nullptr, nullptr}, // Extra slot for -XX:ArchiveHeapTestClass -#endif - {nullptr, nullptr}, -}; - -// full module graph -static ArchivableStaticFieldInfo fmg_archive_subgraph_entry_fields[] = { + // full module graph support {"jdk/internal/loader/ArchivedClassLoaders", "archivedClassLoaders"}, {ARCHIVED_BOOT_LAYER_CLASS, ARCHIVED_BOOT_LAYER_FIELD}, {"java/lang/Module$ArchivedData", "archivedData"}, + +#ifndef PRODUCT + {nullptr, nullptr}, // Extra slot for -XX:ArchiveHeapTestClass +#endif {nullptr, nullptr}, }; @@ -164,8 +161,7 @@ bool HeapShared::is_subgraph_root_class(InstanceKlass* ik) { assert(CDSConfig::is_dumping_heap(), "dump-time only"); if (CDSConfig::is_dumping_klass_subgraphs()) { // Legacy CDS archive support (to be deprecated) - return is_subgraph_root_class_of(archive_subgraph_entry_fields, ik) || - is_subgraph_root_class_of(fmg_archive_subgraph_entry_fields, ik); + return is_subgraph_root_class_of(archive_subgraph_entry_fields, ik); } else { return false; } @@ -175,23 +171,39 @@ oop HeapShared::CachedOopInfo::orig_referrer() const { return _orig_referrer.resolve(); } -unsigned HeapShared::oop_hash(oop const& p) { +// This is a simple hashing of the oop's address. This function is used +// while copying the oops into the AOT heap region. We don't want to +// have any side effects during the copying, so we avoid calling +// p->identity_hash() which can update the object header. +unsigned HeapShared::oop_address_hash(oop const& p) { assert(SafepointSynchronize::is_at_safepoint() || JavaThread::current()->is_in_no_safepoint_scope(), "sanity"); - // Do not call p->identity_hash() as that will update the - // object header. return primitive_hash(cast_from_oop(p)); } -unsigned int HeapShared::oop_handle_hash_raw(const OopHandle& oh) { - return oop_hash(oh.resolve()); -} - -unsigned int HeapShared::oop_handle_hash(const OopHandle& oh) { +// About the hashcode in the cached objects: +// - If a source object has a hashcode, it must be copied into the cache. +// That's because some cached hashtables are laid out using this hashcode. +// - If a source object doesn't have a hashcode, we avoid computing it while +// copying the objects into the cache. This will allow the hashcode to be +// dynamically and randomly computed in each production, which generally +// desirable to make the hashcodes more random between runs. +unsigned HeapShared::archived_object_cache_hash(OopHandle const& oh) { oop o = oh.resolve(); if (o == nullptr) { return 0; + } + if (!_use_identity_hash_for_archived_object_cache) { + // This is called while we are copying the objects. Don't call o->identity_hash() + // as that will update the object header. + return oop_address_hash(o); } else { + // This is called after all objects are copied. It's OK to update + // the object's hashcode. + // + // This may be called after we have left the AOT dumping safepoint. + // Objects in archived_object_cache() may be moved by the GC, so we + // can't use the address of o for computing the hash. return o->identity_hash(); } } @@ -271,6 +283,12 @@ void HeapShared::prepare_for_archiving(TRAPS) { HeapShared::ArchivedObjectCache* HeapShared::_archived_object_cache = nullptr; +// Controls the hashing method for the _archived_object_cache. +// Changes from false to true once, after all objects are copied, +// inside make_archived_object_cache_gc_safe(). +// See archived_object_cache_hash() for more details. +bool HeapShared::_use_identity_hash_for_archived_object_cache = false; + bool HeapShared::is_archived_heap_in_use() { if (HeapShared::is_loading()) { if (HeapShared::is_loading_streaming_mode()) { @@ -384,9 +402,8 @@ void HeapShared::materialize_thread_object() { } } -void HeapShared::add_to_dumped_interned_strings(oop string) { +void HeapShared::archive_interned_string(oop string) { assert(HeapShared::is_writing_mapping_mode(), "Only used by this mode"); - AOTMappedHeapWriter::add_to_dumped_interned_strings(string); bool success = archive_reachable_objects_from(1, _dump_time_special_subgraph, string); assert(success, "shared strings array must not point to arrays or strings that are too large to archive"); } @@ -404,6 +421,24 @@ void HeapShared::finalize_initialization(FileMapInfo* static_mapinfo) { } } +void HeapShared::make_archived_object_cache_gc_safe() { + ArchivedObjectCache* new_cache = new (mtClass)ArchivedObjectCache(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE); + + // It's safe to change the behavior of the hash function now, because iterate_all() + // doesn't call the hash function. + // See archived_object_cache_hash() for more details. + assert(_use_identity_hash_for_archived_object_cache == false, "happens only once"); + _use_identity_hash_for_archived_object_cache = true; + + // Copy all CachedOopInfo into a new table using a different hashing algorithm + archived_object_cache()->iterate_all([&] (OopHandle oh, CachedOopInfo info) { + new_cache->put_when_absent(oh, info); + }); + + destroy_archived_object_cache(); + _archived_object_cache = new_cache; +} + HeapShared::CachedOopInfo* HeapShared::get_cached_oop_info(oop obj) { OopHandle oh(Universe::vm_global(), obj); CachedOopInfo* result = _archived_object_cache->get(oh); @@ -417,14 +452,53 @@ bool HeapShared::has_been_archived(oop obj) { } int HeapShared::append_root(oop obj) { + assert(SafepointSynchronize::is_at_safepoint(), "sanity"); assert(CDSConfig::is_dumping_heap(), "dump-time only"); - if (obj != nullptr) { - assert(has_been_archived(obj), "must be"); - } - // No GC should happen since we aren't scanning _pending_roots. - assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread"); + assert(_pending_roots != nullptr, "sanity"); - return _pending_roots->append(obj); + if (obj == nullptr) { + assert(_pending_roots->at(0) == nullptr, "root index 0 always maps to null"); + return 0; + } else if (CDSConfig::is_dumping_aot_linked_classes()) { + // The AOT compiler may refer the same obj many times, so we + // should use the same index for this oop to avoid excessive entries + // in the roots array. + CachedOopInfo* obj_info = get_cached_oop_info(obj); + assert(obj_info != nullptr, "must be archived"); + + if (obj_info->root_index() > 0) { + return obj_info->root_index(); + } else { + assert(obj_info->root_index() < 0, "must not be zero"); + int i = _pending_roots->append(obj); + obj_info->set_root_index(i); + return i; + } + } else { + return _pending_roots->append(obj); + } +} + +int HeapShared::get_root_index(oop obj) { + if (java_lang_Class::is_instance(obj)) { + obj = scratch_java_mirror(obj); + } + + CachedOopInfo* obj_info = get_cached_oop_info(obj); + const char* error = nullptr; + if (obj_info == nullptr) { + error = "Not a cached oop"; + } else if (obj_info->root_index() < 0) { + error = "Not a cached oop root"; + } else { + return obj_info->root_index(); + } + + ResourceMark rm; + log_debug(aot, codecache, oops)("%s: " INTPTR_FORMAT " (%s)", error, + cast_from_oop(obj), + obj->klass()->external_name()); + return -1; } oop HeapShared::get_root(int index, bool clear) { @@ -453,6 +527,13 @@ void HeapShared::finish_materialize_objects() { } void HeapShared::clear_root(int index) { + if (CDSConfig::is_using_aot_linked_classes()) { + // When AOT linked classes are in use, all roots will be in use all + // the time, there's no benefit for clearing the roots. Also, we + // can't clear the roots as they can be shared. + return; + } + assert(index >= 0, "sanity"); assert(CDSConfig::is_using_archive(), "must be"); if (is_archived_heap_in_use()) { @@ -600,9 +681,10 @@ objArrayOop HeapShared::scratch_resolved_references(ConstantPool* src) { return (objArrayOop)_scratch_objects_table->get_oop(src); } - void HeapShared::init_dumping() { - _scratch_objects_table = new (mtClass)MetaspaceObjToOopHandleTable(); - _pending_roots = new GrowableArrayCHeap(500); +void HeapShared::init_dumping() { + _scratch_objects_table = new (mtClass)MetaspaceObjToOopHandleTable(); + _pending_roots = new GrowableArrayCHeap(500); + _pending_roots->append(nullptr); // root index 0 represents a null oop } void HeapShared::init_scratch_objects_for_basic_type_mirrors(TRAPS) { @@ -839,7 +921,7 @@ void HeapShared::start_scanning_for_oops() { // The special subgraph doesn't belong to any class. We use Object_klass() here just // for convenience. - _dump_time_special_subgraph = init_subgraph_info(vmClasses::Object_klass(), false); + _dump_time_special_subgraph = init_subgraph_info(vmClasses::Object_klass()); // Cache for recording where the archived objects are copied to create_archived_object_cache(); @@ -883,6 +965,11 @@ void HeapShared::write_heap(AOTMappedHeapInfo* mapped_heap_info, AOTStreamedHeap ArchiveBuilder::OtherROAllocMark mark; write_subgraph_info_table(); + + delete _pending_roots; + _pending_roots = nullptr; + + make_archived_object_cache_gc_safe(); } void HeapShared::scan_java_mirror(oop orig_mirror) { @@ -912,12 +999,7 @@ void HeapShared::archive_subgraphs() { assert(CDSConfig::is_dumping_heap(), "must be"); if (CDSConfig::is_dumping_klass_subgraphs()) { - archive_object_subgraphs(archive_subgraph_entry_fields, - false /* is_full_module_graph */); - if (CDSConfig::is_dumping_full_module_graph()) { - archive_object_subgraphs(fmg_archive_subgraph_entry_fields, - true /* is_full_module_graph */); - } + archive_object_subgraphs(archive_subgraph_entry_fields); } } @@ -930,12 +1012,11 @@ HeapShared::RunTimeKlassSubGraphInfoTable HeapShared::_run_time_subgraph_info_ // Get the subgraph_info for Klass k. A new subgraph_info is created if // there is no existing one for k. The subgraph_info records the "buffered" // address of the class. -KlassSubGraphInfo* HeapShared::init_subgraph_info(Klass* k, bool is_full_module_graph) { +KlassSubGraphInfo* HeapShared::init_subgraph_info(Klass* k) { assert(CDSConfig::is_dumping_heap(), "dump time only"); bool created; KlassSubGraphInfo* info = - _dump_time_subgraph_info_table->put_if_absent(k, KlassSubGraphInfo(k, is_full_module_graph), - &created); + _dump_time_subgraph_info_table->put_if_absent(k, KlassSubGraphInfo(k), &created); assert(created, "must not initialize twice"); return info; } @@ -1023,7 +1104,6 @@ void KlassSubGraphInfo::add_subgraph_object_klass(Klass* orig_k) { } _subgraph_object_klasses->append_if_missing(orig_k); - _has_non_early_klasses |= is_non_early_klass(orig_k); } void KlassSubGraphInfo::check_allowed_klass(InstanceKlass* ik) { @@ -1066,45 +1146,11 @@ void KlassSubGraphInfo::check_allowed_klass(InstanceKlass* ik) { AOTMetaspace::unrecoverable_writing_error(); } -bool KlassSubGraphInfo::is_non_early_klass(Klass* k) { - if (k->is_objArray_klass()) { - k = ObjArrayKlass::cast(k)->bottom_klass(); - } - if (k->is_instance_klass()) { - if (!SystemDictionaryShared::is_early_klass(InstanceKlass::cast(k))) { - ResourceMark rm; - log_info(aot, heap)("non-early: %s", k->external_name()); - return true; - } else { - return false; - } - } else { - return false; - } -} - // Initialize an archived subgraph_info_record from the given KlassSubGraphInfo. void ArchivedKlassSubGraphInfoRecord::init(KlassSubGraphInfo* info) { _k = ArchiveBuilder::get_buffered_klass(info->klass()); _entry_field_records = nullptr; _subgraph_object_klasses = nullptr; - _is_full_module_graph = info->is_full_module_graph(); - - if (_is_full_module_graph) { - // Consider all classes referenced by the full module graph as early -- we will be - // allocating objects of these classes during JVMTI early phase, so they cannot - // be processed by (non-early) JVMTI ClassFileLoadHook - _has_non_early_klasses = false; - } else { - _has_non_early_klasses = info->has_non_early_klasses(); - } - - if (_has_non_early_klasses) { - ResourceMark rm; - log_info(aot, heap)( - "Subgraph of klass %s has non-early klasses and cannot be used when JVMTI ClassFileLoadHook is enabled", - _k->external_name()); - } // populate the entry fields GrowableArray* entry_fields = info->subgraph_entry_fields(); @@ -1262,15 +1308,10 @@ static void verify_the_heap(Klass* k, const char* which) { // Before GC can execute, we must ensure that all oops reachable from HeapShared::roots() // have a valid klass. I.e., oopDesc::klass() must have already been resolved. -// -// Note: if a ArchivedKlassSubGraphInfoRecord contains non-early classes, and JVMTI -// ClassFileLoadHook is enabled, it's possible for this class to be dynamically replaced. In -// this case, we will not load the ArchivedKlassSubGraphInfoRecord and will clear its roots. void HeapShared::resolve_classes(JavaThread* current) { assert(CDSConfig::is_using_archive(), "runtime only!"); if (CDSConfig::is_using_klass_subgraphs()) { resolve_classes_for_subgraphs(current, archive_subgraph_entry_fields); - resolve_classes_for_subgraphs(current, fmg_archive_subgraph_entry_fields); } } @@ -1418,24 +1459,6 @@ HeapShared::resolve_or_init_classes_for_subgraph_of(Klass* k, bool do_init, TRAP } return nullptr; } else { - if (record->is_full_module_graph() && !CDSConfig::is_using_full_module_graph()) { - if (log_is_enabled(Info, aot, heap)) { - ResourceMark rm(THREAD); - log_info(aot, heap)("subgraph %s cannot be used because full module graph is disabled", - k->external_name()); - } - return nullptr; - } - - if (record->has_non_early_klasses() && JvmtiExport::should_post_class_file_load_hook()) { - if (log_is_enabled(Info, aot, heap)) { - ResourceMark rm(THREAD); - log_info(aot, heap)("subgraph %s cannot be used because JVMTI ClassFileLoadHook is enabled", - k->external_name()); - } - return nullptr; - } - if (log_is_enabled(Info, aot, heap)) { ResourceMark rm; log_info(aot, heap)("%s subgraph %s ", do_init ? "init" : "resolve", k->external_name()); @@ -1517,8 +1540,8 @@ void HeapShared::init_archived_fields_for(Klass* k, const ArchivedKlassSubGraphI // mirror after this point. if (log_is_enabled(Info, aot, heap)) { ResourceMark rm; - log_info(aot, heap)("initialize_from_archived_subgraph %s " PTR_FORMAT "%s%s", - k->external_name(), p2i(k), JvmtiExport::is_early_phase() ? " (early)" : "", + log_info(aot, heap)("initialize_from_archived_subgraph %s " PTR_FORMAT "%s", + k->external_name(), p2i(k), k->has_aot_initialized_mirror() ? " (aot-inited)" : ""); } } @@ -1911,6 +1934,11 @@ void HeapShared::verify_subgraph_from(oop orig_obj) { void HeapShared::verify_reachable_objects_from(oop obj) { _num_total_verifications ++; if (java_lang_Class::is_instance(obj)) { + Klass* k = java_lang_Class::as_Klass(obj); + if (RegeneratedClasses::has_been_regenerated(k)) { + k = RegeneratedClasses::get_regenerated_object(k); + obj = k->java_mirror(); + } obj = scratch_java_mirror(obj); assert(obj != nullptr, "must be"); } @@ -1979,9 +2007,9 @@ void HeapShared::set_has_been_seen_during_subgraph_recording(oop obj) { ++ _num_new_walked_objs; } -void HeapShared::start_recording_subgraph(InstanceKlass *k, const char* class_name, bool is_full_module_graph) { +void HeapShared::start_recording_subgraph(InstanceKlass *k, const char* class_name) { log_info(aot, heap)("Start recording subgraph(s) for archived fields in %s", class_name); - init_subgraph_info(k, is_full_module_graph); + init_subgraph_info(k); init_seen_objects_table(); _num_new_walked_objs = 0; _num_new_archived_objs = 0; @@ -2113,9 +2141,6 @@ void HeapShared::init_subgraph_entry_fields(TRAPS) { _dump_time_subgraph_info_table = new (mtClass)DumpTimeKlassSubGraphInfoTable(); if (CDSConfig::is_dumping_klass_subgraphs()) { init_subgraph_entry_fields(archive_subgraph_entry_fields, CHECK); - if (CDSConfig::is_dumping_full_module_graph()) { - init_subgraph_entry_fields(fmg_archive_subgraph_entry_fields, CHECK); - } } } @@ -2214,8 +2239,7 @@ void HeapShared::init_heap_writer() { } } -void HeapShared::archive_object_subgraphs(ArchivableStaticFieldInfo fields[], - bool is_full_module_graph) { +void HeapShared::archive_object_subgraphs(ArchivableStaticFieldInfo fields[]) { _num_total_subgraph_recordings = 0; _num_total_walked_objs = 0; _num_total_archived_objs = 0; @@ -2231,7 +2255,7 @@ void HeapShared::archive_object_subgraphs(ArchivableStaticFieldInfo fields[], for (int i = 0; fields[i].valid(); ) { ArchivableStaticFieldInfo* info = &fields[i]; const char* klass_name = info->klass_name; - start_recording_subgraph(info->klass, klass_name, is_full_module_graph); + start_recording_subgraph(info->klass, klass_name); // If you have specified consecutive fields of the same klass in // fields[], these will be archived in the same @@ -2264,12 +2288,22 @@ void HeapShared::archive_object_subgraphs(ArchivableStaticFieldInfo fields[], #endif } -bool HeapShared::is_dumped_interned_string(oop o) { - if (is_writing_mapping_mode()) { - return AOTMappedHeapWriter::is_dumped_interned_string(o); - } else { - return AOTStreamedHeapWriter::is_dumped_interned_string(o); +bool HeapShared::is_interned_string(oop obj) { + if (!java_lang_String::is_instance(obj)) { + return false; } + + ResourceMark rm; + int len = 0; + jchar* name = java_lang_String::as_unicode_string_or_null(obj, len); + if (name == nullptr) { + fatal("Insufficient memory for dumping"); + } + return StringTable::lookup(name, len) == obj; +} + +bool HeapShared::is_dumped_interned_string(oop o) { + return is_interned_string(o) && has_been_archived(o); } // These tables should be used only within the CDS safepoint, so diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index 2cb330160e4..c3ad1f666b1 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -40,7 +40,6 @@ #include "utilities/hashTable.hpp" #if INCLUDE_CDS_JAVA_HEAP -class DumpedInternedStrings; class FileMapInfo; class KlassSubGraphInfo; class MetaspaceObjToOopHandleTable; @@ -67,21 +66,12 @@ class KlassSubGraphInfo: public CHeapObj { // For each entry field, it is a tuple of field_offset, field_value GrowableArray* _subgraph_entry_fields; - // Does this KlassSubGraphInfo belong to the archived full module graph - bool _is_full_module_graph; - - // Does this KlassSubGraphInfo references any classes that were loaded while - // JvmtiExport::is_early_phase()!=true. If so, this KlassSubGraphInfo cannot be - // used at runtime if JVMTI ClassFileLoadHook is enabled. - bool _has_non_early_klasses; static bool is_non_early_klass(Klass* k); static void check_allowed_klass(InstanceKlass* ik); public: - KlassSubGraphInfo(Klass* k, bool is_full_module_graph) : + KlassSubGraphInfo(Klass* k) : _k(k), _subgraph_object_klasses(nullptr), - _subgraph_entry_fields(nullptr), - _is_full_module_graph(is_full_module_graph), - _has_non_early_klasses(false) {} + _subgraph_entry_fields(nullptr) {} ~KlassSubGraphInfo() { if (_subgraph_object_klasses != nullptr) { @@ -105,8 +95,6 @@ class KlassSubGraphInfo: public CHeapObj { return _subgraph_object_klasses == nullptr ? 0 : _subgraph_object_klasses->length(); } - bool is_full_module_graph() const { return _is_full_module_graph; } - bool has_non_early_klasses() const { return _has_non_early_klasses; } }; // An archived record of object sub-graphs reachable from static @@ -115,7 +103,6 @@ class KlassSubGraphInfo: public CHeapObj { class ArchivedKlassSubGraphInfoRecord { private: Klass* _k; - bool _is_full_module_graph; bool _has_non_early_klasses; // contains pairs of field offset and value for each subgraph entry field @@ -131,7 +118,6 @@ class ArchivedKlassSubGraphInfoRecord { Klass* klass() const { return _k; } Array* entry_field_records() const { return _entry_field_records; } Array* subgraph_object_klasses() const { return _subgraph_object_klasses; } - bool is_full_module_graph() const { return _is_full_module_graph; } bool has_non_early_klasses() const { return _has_non_early_klasses; } }; #endif // INCLUDE_CDS_JAVA_HEAP @@ -176,7 +162,7 @@ public: static void initialize_streaming() NOT_CDS_JAVA_HEAP_RETURN; static void enable_gc() NOT_CDS_JAVA_HEAP_RETURN; static void materialize_thread_object() NOT_CDS_JAVA_HEAP_RETURN; - static void add_to_dumped_interned_strings(oop string) NOT_CDS_JAVA_HEAP_RETURN; + static void archive_interned_string(oop string); static void finalize_initialization(FileMapInfo* static_mapinfo) NOT_CDS_JAVA_HEAP_RETURN; private: @@ -195,13 +181,8 @@ private: static void print_stats(); public: static void debug_trace(); - static unsigned oop_hash(oop const& p); - static unsigned oop_handle_hash(OopHandle const& oh); - static unsigned oop_handle_hash_raw(OopHandle const& oh); + static unsigned oop_address_hash(oop const& p); static bool oop_handle_equals(const OopHandle& a, const OopHandle& b); - static unsigned string_oop_hash(oop const& string) { - return java_lang_String::hash_code(string); - } class CopyKlassSubGraphInfoToArchive; @@ -217,27 +198,37 @@ public: // One or more fields in this object are pointing to MetaspaceObj bool _has_native_pointers; + + // >= 0 if this oop has been append to the list of roots + int _root_index; public: CachedOopInfo(OopHandle orig_referrer, bool has_oop_pointers) : _orig_referrer(orig_referrer), _buffer_offset(0), _has_oop_pointers(has_oop_pointers), - _has_native_pointers(false) {} + _has_native_pointers(false), + _root_index(-1) {} oop orig_referrer() const; void set_buffer_offset(size_t offset) { _buffer_offset = offset; } size_t buffer_offset() const { return _buffer_offset; } bool has_oop_pointers() const { return _has_oop_pointers; } bool has_native_pointers() const { return _has_native_pointers; } void set_has_native_pointers() { _has_native_pointers = true; } + int root_index() const { return _root_index; } + void set_root_index(int i) { _root_index = i; } }; private: static const int INITIAL_TABLE_SIZE = 15889; // prime number static const int MAX_TABLE_SIZE = 1000000; + static bool _use_identity_hash_for_archived_object_cache; + + static unsigned archived_object_cache_hash(OopHandle const& oh); + typedef ResizeableHashTable ArchivedObjectCache; static ArchivedObjectCache* _archived_object_cache; @@ -266,8 +257,7 @@ private: static CachedOopInfo make_cached_oop_info(oop obj, oop referrer); static ArchivedKlassSubGraphInfoRecord* archive_subgraph_info(KlassSubGraphInfo* info); - static void archive_object_subgraphs(ArchivableStaticFieldInfo fields[], - bool is_full_module_graph); + static void archive_object_subgraphs(ArchivableStaticFieldInfo fields[]); // Archive object sub-graph starting from the given static field // in Klass k's mirror. @@ -281,7 +271,7 @@ private: static void verify_subgraph_from(oop orig_obj) PRODUCT_RETURN; static void check_special_subgraph_classes(); - static KlassSubGraphInfo* init_subgraph_info(Klass *k, bool is_full_module_graph); + static KlassSubGraphInfo* init_subgraph_info(Klass *k); static KlassSubGraphInfo* get_subgraph_info(Klass *k); static void init_subgraph_entry_fields(TRAPS) NOT_CDS_JAVA_HEAP_RETURN; @@ -297,7 +287,7 @@ private: typedef ResizeableHashTable SeenObjectsTable; + HeapShared::oop_address_hash> SeenObjectsTable; static SeenObjectsTable *_seen_objects_table; @@ -336,8 +326,7 @@ private: static size_t _num_total_recorded_klasses; static size_t _num_total_verifications; - static void start_recording_subgraph(InstanceKlass *k, const char* klass_name, - bool is_full_module_graph); + static void start_recording_subgraph(InstanceKlass *k, const char* klass_name); static void done_recording_subgraph(InstanceKlass *k, const char* klass_name); static bool has_been_seen_during_subgraph_recording(oop obj); @@ -394,6 +383,7 @@ private: delete _archived_object_cache; _archived_object_cache = nullptr; } + static void make_archived_object_cache_gc_safe(); static ArchivedObjectCache* archived_object_cache() { return _archived_object_cache; } @@ -406,6 +396,7 @@ private: KlassSubGraphInfo* subgraph_info, oop orig_obj); + static bool is_interned_string(oop obj); static bool is_dumped_interned_string(oop o); // Scratch objects for archiving Klass::java_mirror() @@ -437,6 +428,11 @@ private: // Dump-time only. Returns the index of the root, which can be used at run time to read // the root using get_root(index, ...). static int append_root(oop obj); + + // AOT-compile time only. + // Returns -1 if obj is not in the heap root set. + static int get_root_index(oop obj) NOT_CDS_JAVA_HEAP_RETURN_(-1); + static GrowableArrayCHeap* pending_roots() { return _pending_roots; } // Dump-time and runtime @@ -445,9 +441,7 @@ private: // Run-time only static void clear_root(int index); - static void get_segment_indexes(int index, int& segment_index, int& internal_index); - static void setup_test_class(const char* test_class_name) PRODUCT_RETURN; #endif // INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/classfile/classPrinter.cpp b/src/hotspot/share/classfile/classPrinter.cpp index 3ed0a5e9840..6cf89f7357f 100644 --- a/src/hotspot/share/classfile/classPrinter.cpp +++ b/src/hotspot/share/classfile/classPrinter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -224,6 +224,7 @@ void ClassPrinter::print_flags_help(outputStream* os) { os->print_cr(" 0x%02x - print info for invokehandle", PRINT_METHOD_HANDLE); os->print_cr(" 0x%02x - print details of the C++ and Java objects that represent classes", PRINT_CLASS_DETAILS); os->print_cr(" 0x%02x - print details of the C++ objects that represent methods", PRINT_METHOD_DETAILS); + os->print_cr(" 0x%02x - print MethodData", PRINT_METHOD_DATA); os->cr(); } diff --git a/src/hotspot/share/classfile/classPrinter.hpp b/src/hotspot/share/classfile/classPrinter.hpp index 470e82ddc0e..b09a1a1ef3b 100644 --- a/src/hotspot/share/classfile/classPrinter.hpp +++ b/src/hotspot/share/classfile/classPrinter.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -54,6 +54,7 @@ public: PRINT_METHOD_HANDLE = 1 << 4, // extra information for invokehandle PRINT_CLASS_DETAILS = 1 << 5, // print details of the C++ and Java objects that represent classes PRINT_METHOD_DETAILS = 1 << 6, // print details of the C++ objects that represent methods + PRINT_METHOD_DATA = 1 << 7, // print MethodData - requires MDO lock }; static bool has_mode(int flags, Mode mode) { return (flags & static_cast(mode)) != 0; diff --git a/src/hotspot/share/classfile/stringTable.cpp b/src/hotspot/share/classfile/stringTable.cpp index 2b8b7780a41..c3f60487b9c 100644 --- a/src/hotspot/share/classfile/stringTable.cpp +++ b/src/hotspot/share/classfile/stringTable.cpp @@ -946,7 +946,7 @@ void StringTable::init_shared_table() { // so we are all good. // - If there's a reference to it, we will report an error inside HeapShared.cpp and // dumping will fail. - HeapShared::add_to_dumped_interned_strings(string); + HeapShared::archive_interned_string(string); } n++; return true; diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index cfb20412ab8..5947050e31a 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -373,11 +373,6 @@ bool SystemDictionaryShared::is_jfr_event_class(InstanceKlass *k) { return false; } -bool SystemDictionaryShared::is_early_klass(InstanceKlass* ik) { - DumpTimeClassInfo* info = _dumptime_table->get(ik); - return (info != nullptr) ? info->is_early_klass() : false; -} - bool SystemDictionaryShared::check_self_exclusion(InstanceKlass* k) { bool log_warning = false; const char* error = check_self_exclusion_helper(k, log_warning); diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp index 2619a642fd1..33b245e26fc 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -199,7 +199,6 @@ private: static void iterate_verification_constraint_names(InstanceKlass* k, DumpTimeClassInfo* info, Function func); public: - static bool is_early_klass(InstanceKlass* k); // Was k loaded while JvmtiExport::is_early_phase()==true static bool has_archived_enum_objs(InstanceKlass* ik); static void set_has_archived_enum_objs(InstanceKlass* ik); diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index 76d09161fdd..48be24c20dc 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -1607,12 +1607,12 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) { case Bytecodes::_if_acmpeq : case Bytecodes::_if_acmpne : current_frame.pop_stack( - VerificationType::reference_check(), CHECK_VERIFY(this)); + object_type(), CHECK_VERIFY(this)); // fall through case Bytecodes::_ifnull : case Bytecodes::_ifnonnull : current_frame.pop_stack( - VerificationType::reference_check(), CHECK_VERIFY(this)); + object_type(), CHECK_VERIFY(this)); stackmap_table.check_jump_target (¤t_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this)); no_control_flow = false; break; diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 67817682ced..e84acd62284 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -368,10 +368,10 @@ class methodHandle; do_intrinsic(_inflateStringB, java_lang_StringLatin1, inflate_name, inflateB_signature, F_S) \ do_signature(inflateB_signature, "([BI[BII)V") \ do_intrinsic(_toBytesStringU, java_lang_StringUTF16, toBytes_name, toBytesU_signature, F_S) \ - do_name( toBytes_name, "toBytes") \ + do_name( toBytes_name, "toBytes0") \ do_signature(toBytesU_signature, "([CII)[B") \ do_intrinsic(_getCharsStringU, java_lang_StringUTF16, getCharsU_name, getCharsU_signature, F_S) \ - do_name( getCharsU_name, "getChars") \ + do_name( getCharsU_name, "getChars0") \ do_signature(getCharsU_signature, "([BII[CI)V") \ do_intrinsic(_getCharStringU, java_lang_StringUTF16, getChar_name, getCharStringU_signature, F_S) \ do_signature(getCharStringU_signature, "([BI)C") \ diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index e5f68afc51d..b29cf906736 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -284,11 +284,11 @@ bool AOTCodeCache::open_cache(bool is_dumping, bool is_using) { return true; } -void AOTCodeCache::close() { +void AOTCodeCache::dump() { if (is_on()) { - delete _cache; // Free memory - _cache = nullptr; - opened_cache = nullptr; + assert(is_on_for_dump(), "should be called only when dumping AOT code"); + MutexLocker ml(Compile_lock); + _cache->finish_write(); } } @@ -304,7 +304,6 @@ AOTCodeCache::AOTCodeCache(bool is_dumping, bool is_using) : _store_size(0), _for_use(is_using), _for_dump(is_dumping), - _closing(false), _failed(false), _lookup_failed(false), _table(nullptr), @@ -381,30 +380,6 @@ void AOTCodeCache::init_early_c1_table() { } } -AOTCodeCache::~AOTCodeCache() { - if (_closing) { - return; // Already closed - } - // Stop any further access to cache. - _closing = true; - - MutexLocker ml(Compile_lock); - if (for_dump()) { // Finalize cache - finish_write(); - } - _load_buffer = nullptr; - if (_C_store_buffer != nullptr) { - FREE_C_HEAP_ARRAY(char, _C_store_buffer); - _C_store_buffer = nullptr; - _store_buffer = nullptr; - } - if (_table != nullptr) { - MutexLocker ml(AOTCodeCStrings_lock, Mutex::_no_safepoint_check_flag); - delete _table; - _table = nullptr; - } -} - void AOTCodeCache::Config::record(uint cpu_features_offset) { _flags = 0; #ifdef ASSERT @@ -1545,18 +1520,6 @@ void AOTCodeAddressTable::init_early_c1() { #undef SET_ADDRESS -AOTCodeAddressTable::~AOTCodeAddressTable() { - if (_extrs_addr != nullptr) { - FREE_C_HEAP_ARRAY(address, _extrs_addr); - } - if (_stubs_addr != nullptr) { - FREE_C_HEAP_ARRAY(address, _stubs_addr); - } - if (_shared_blobs_addr != nullptr) { - FREE_C_HEAP_ARRAY(address, _shared_blobs_addr); - } -} - #ifdef PRODUCT #define MAX_STR_COUNT 200 #else diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp index 85f8b47920f..bd8721fea8c 100644 --- a/src/hotspot/share/code/aotCodeCache.hpp +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -152,7 +152,6 @@ public: _early_c1_complete(false), _complete(false) { } - ~AOTCodeAddressTable(); void init_extrs(); void init_early_stubs(); void init_shared_blobs(); @@ -260,7 +259,6 @@ private: uint _store_size; // Used when writing cache bool _for_use; // AOT cache is open for using AOT code bool _for_dump; // AOT cache is open for dumping AOT code - bool _closing; // Closing cache file bool _failed; // Failed read/write to/from cache (cache is broken?) bool _lookup_failed; // Failed to lookup for info (skip only this code load) @@ -290,7 +288,6 @@ private: public: AOTCodeCache(bool is_dumping, bool is_using); - ~AOTCodeCache(); const char* cache_buffer() const { return _load_buffer; } bool failed() const { return _failed; } @@ -314,8 +311,6 @@ public: bool for_use() const { return _for_use && !_failed; } bool for_dump() const { return _for_dump && !_failed; } - bool closing() const { return _closing; } - AOTCodeEntry* add_entry() { _store_entries_cnt++; _store_entries -= 1; @@ -375,8 +370,8 @@ public: static AOTCodeCache* cache() { assert(_passed_init2, "Too early to ask"); return _cache; } static void initialize() NOT_CDS_RETURN; static void init2() NOT_CDS_RETURN; - static void close() NOT_CDS_RETURN; - static bool is_on() CDS_ONLY({ return cache() != nullptr && !_cache->closing(); }) NOT_CDS_RETURN_(false); + static void dump() NOT_CDS_RETURN; + static bool is_on() CDS_ONLY({ return cache() != nullptr; }) NOT_CDS_RETURN_(false); static bool is_on_for_use() CDS_ONLY({ return is_on() && _cache->for_use(); }) NOT_CDS_RETURN_(false); static bool is_on_for_dump() CDS_ONLY({ return is_on() && _cache->for_dump(); }) NOT_CDS_RETURN_(false); static bool is_dumping_stub() NOT_CDS_RETURN_(false); diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 4c2f9157b99..5a6ed8ab3ed 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -1306,9 +1306,7 @@ nmethod::nmethod( _deopt_handler_entry_offset = 0; _unwind_handler_offset = 0; - CHECKED_CAST(_oops_size, uint16_t, align_up(code_buffer->total_oop_size(), oopSize)); - uint16_t metadata_size; - CHECKED_CAST(metadata_size, uint16_t, align_up(code_buffer->total_metadata_size(), wordSize)); + int metadata_size = align_up(code_buffer->total_metadata_size(), wordSize); JVMCI_ONLY( _metadata_size = metadata_size; ) assert(_mutable_data_size == _relocation_size + metadata_size, "wrong mutable data size: %d != %d + %d", @@ -1446,7 +1444,6 @@ nmethod::nmethod(const nmethod &nm) : CodeBlob(nm._name, nm._kind, nm._size, nm. _deopt_handler_entry_offset = nm._deopt_handler_entry_offset; _unwind_handler_offset = nm._unwind_handler_offset; _num_stack_arg_slots = nm._num_stack_arg_slots; - _oops_size = nm._oops_size; #if INCLUDE_JVMCI _metadata_size = nm._metadata_size; #endif @@ -1749,9 +1746,7 @@ nmethod::nmethod( _unwind_handler_offset = -1; } - CHECKED_CAST(_oops_size, uint16_t, align_up(code_buffer->total_oop_size(), oopSize)); - uint16_t metadata_size; - CHECKED_CAST(metadata_size, uint16_t, align_up(code_buffer->total_metadata_size(), wordSize)); + int metadata_size = align_up(code_buffer->total_metadata_size(), wordSize); JVMCI_ONLY( _metadata_size = metadata_size; ) int jvmci_data_size = 0 JVMCI_ONLY( + align_up(compiler->is_jvmci() ? jvmci_data->size() : 0, oopSize)); assert(_mutable_data_size == _relocation_size + metadata_size + jvmci_data_size, diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index 2391bc6d830..092da181f12 100644 --- a/src/hotspot/share/code/nmethod.hpp +++ b/src/hotspot/share/code/nmethod.hpp @@ -235,11 +235,10 @@ class nmethod : public CodeBlob { // Number of arguments passed on the stack uint16_t _num_stack_arg_slots; - uint16_t _oops_size; #if INCLUDE_JVMCI // _metadata_size is not specific to JVMCI. In the non-JVMCI case, it can be derived as: // _metadata_size = mutable_data_size - relocation_size - uint16_t _metadata_size; + int _metadata_size; #endif // Offset in immutable data section diff --git a/src/hotspot/share/compiler/compilerDefinitions.cpp b/src/hotspot/share/compiler/compilerDefinitions.cpp index b8244036835..0e4e211453b 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.cpp +++ b/src/hotspot/share/compiler/compilerDefinitions.cpp @@ -185,49 +185,6 @@ intx CompilerConfig::scaled_freq_log(intx freq_log, double scale) { } } -void CompilerConfig::set_client_emulation_mode_flags() { - assert(has_c1(), "Must have C1 compiler present"); - CompilationModeFlag::set_quick_only(); - - FLAG_SET_ERGO(ProfileInterpreter, false); -#if INCLUDE_JVMCI - FLAG_SET_ERGO(EnableJVMCI, false); - FLAG_SET_ERGO(UseJVMCICompiler, false); -#endif - if (FLAG_IS_DEFAULT(NeverActAsServerClassMachine)) { - FLAG_SET_ERGO(NeverActAsServerClassMachine, true); - } - if (FLAG_IS_DEFAULT(InitialCodeCacheSize)) { - FLAG_SET_ERGO(InitialCodeCacheSize, 160*K); - } - if (FLAG_IS_DEFAULT(ReservedCodeCacheSize)) { - FLAG_SET_ERGO(ReservedCodeCacheSize, 32*M); - } - if (FLAG_IS_DEFAULT(NonProfiledCodeHeapSize)) { - FLAG_SET_ERGO(NonProfiledCodeHeapSize, 27*M); - } - if (FLAG_IS_DEFAULT(ProfiledCodeHeapSize)) { - FLAG_SET_ERGO(ProfiledCodeHeapSize, 0); - } - if (FLAG_IS_DEFAULT(NonNMethodCodeHeapSize)) { - FLAG_SET_ERGO(NonNMethodCodeHeapSize, 5*M); - } - if (FLAG_IS_DEFAULT(CodeCacheExpansionSize)) { - FLAG_SET_ERGO(CodeCacheExpansionSize, 32*K); - } - if (FLAG_IS_DEFAULT(CICompilerCount)) { - FLAG_SET_ERGO(CICompilerCount, 1); - } -} - -bool CompilerConfig::is_compilation_mode_selected() { - return !FLAG_IS_DEFAULT(TieredCompilation) || - !FLAG_IS_DEFAULT(TieredStopAtLevel) || - !FLAG_IS_DEFAULT(CompilationMode) - JVMCI_ONLY(|| !FLAG_IS_DEFAULT(EnableJVMCI) - || !FLAG_IS_DEFAULT(UseJVMCICompiler)); -} - static bool check_legacy_flags() { JVMFlag* compile_threshold_flag = JVMFlag::flag_from_enum(FLAG_MEMBER_ENUM(CompileThreshold)); if (JVMFlagAccess::check_constraint(compile_threshold_flag, JVMFlagLimit::get_constraint(compile_threshold_flag)->constraint_func(), false) != JVMFlag::SUCCESS) { @@ -543,36 +500,11 @@ bool CompilerConfig::check_args_consistency(bool status) { return status; } -bool CompilerConfig::should_set_client_emulation_mode_flags() { -#if !COMPILER1_OR_COMPILER2 - return false; -#endif - - if (has_c1()) { - if (!is_compilation_mode_selected()) { - if (NeverActAsServerClassMachine) { - return true; - } - } else if (!has_c2() && !is_jvmci_compiler()) { - return true; - } - } - - return false; -} - void CompilerConfig::ergo_initialize() { #if !COMPILER1_OR_COMPILER2 return; #endif - // This property is also checked when selecting the heap size. Since client - // emulation mode influences Java heap memory usage, part of the logic must - // occur before choosing the heap size. - if (should_set_client_emulation_mode_flags()) { - set_client_emulation_mode_flags(); - } - set_legacy_emulation_flags(); set_compilation_policy_flags(); @@ -591,9 +523,6 @@ void CompilerConfig::ergo_initialize() { } if (ProfileInterpreter && CompilerConfig::is_c1_simple_only()) { - if (!FLAG_IS_DEFAULT(ProfileInterpreter)) { - warning("ProfileInterpreter disabled due to client emulation mode"); - } FLAG_SET_CMDLINE(ProfileInterpreter, false); } diff --git a/src/hotspot/share/compiler/compilerDefinitions.hpp b/src/hotspot/share/compiler/compilerDefinitions.hpp index a9b052ff782..e8ba977f705 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.hpp +++ b/src/hotspot/share/compiler/compilerDefinitions.hpp @@ -151,14 +151,10 @@ public: inline static CompilerType compiler_type(); - static bool should_set_client_emulation_mode_flags(); - private: - static bool is_compilation_mode_selected(); static void set_compilation_policy_flags(); static void set_jvmci_specific_flags(); static void set_legacy_emulation_flags(); - static void set_client_emulation_mode_flags(); }; #endif // SHARE_COMPILER_COMPILERDEFINITIONS_HPP diff --git a/src/hotspot/share/compiler/compiler_globals_pd.hpp b/src/hotspot/share/compiler/compiler_globals_pd.hpp index 537a7f030fb..8ac4b53d6cd 100644 --- a/src/hotspot/share/compiler/compiler_globals_pd.hpp +++ b/src/hotspot/share/compiler/compiler_globals_pd.hpp @@ -69,13 +69,6 @@ define_pd_global(size_t, NonNMethodCodeHeapSize, 32*M); define_pd_global(size_t, CodeCacheExpansionSize, 32*K); define_pd_global(size_t, CodeCacheMinBlockLength, 1); define_pd_global(size_t, CodeCacheMinimumUseSpace, 200*K); -#ifndef ZERO -define_pd_global(bool, NeverActAsServerClassMachine, true); -#else -// Zero runs without compilers. Do not let this code to force -// the GC mode and default heap settings. -define_pd_global(bool, NeverActAsServerClassMachine, false); -#endif #define CI_COMPILER_COUNT 0 #else diff --git a/src/hotspot/share/compiler/disassembler.hpp b/src/hotspot/share/compiler/disassembler.hpp index db7066c9023..b9de9c3d27d 100644 --- a/src/hotspot/share/compiler/disassembler.hpp +++ b/src/hotspot/share/compiler/disassembler.hpp @@ -112,7 +112,7 @@ class Disassembler : public AbstractDisassembler { // interpreter code, by riding on the customary __ macro in the interpreter generator. // See templateTable_x86.cpp for an example. template inline static T* hook(const char* file, int line, T* masm) { - if (PrintInterpreter) { + if (PrintInterpreter NOT_PRODUCT(|| true)) { _hook(file, line, masm); } return masm; diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index abc45254782..be9ecf19123 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -42,7 +42,6 @@ #include "gc/g1/g1FullCollector.hpp" #include "gc/g1/g1GCCounters.hpp" #include "gc/g1/g1GCParPhaseTimesTracker.hpp" -#include "gc/g1/g1GCPauseType.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "gc/g1/g1HeapRegionPrinter.hpp" @@ -2481,7 +2480,7 @@ void G1CollectedHeap::trace_heap(GCWhen::Type when, const GCTracer* gc_tracer) { void G1CollectedHeap::gc_prologue(bool full) { // Update common counters. increment_total_collections(full /* full gc */); - if (full || collector_state()->in_concurrent_start_gc()) { + if (full || collector_state()->is_in_concurrent_start_gc()) { increment_old_marking_cycles_started(); } } @@ -2554,7 +2553,7 @@ HeapWord* G1CollectedHeap::do_collection_pause(size_t word_size, void G1CollectedHeap::start_concurrent_cycle(bool concurrent_operation_is_full_mark) { assert(_cm->is_fully_initialized(), "sanity"); - assert(!_cm->in_progress(), "Can not start concurrent operation while in progress"); + assert(!collector_state()->is_in_concurrent_cycle(), "Can not start concurrent cycle when already running"); MutexLocker x(G1CGC_lock, Mutex::_no_safepoint_check_flag); if (concurrent_operation_is_full_mark) { _cm->start_full_concurrent_cycle(); @@ -2651,7 +2650,7 @@ void G1CollectedHeap::verify_after_young_collection(G1HeapVerifier::G1VerifyType verify_numa_regions("GC End"); _verifier->verify_region_sets_optional(); - if (collector_state()->in_concurrent_start_gc()) { + if (collector_state()->is_in_concurrent_start_gc()) { log_debug(gc, verify)("Marking state"); _verifier->verify_marking_state(); } @@ -2732,7 +2731,7 @@ void G1CollectedHeap::do_collection_pause_at_safepoint(size_t allocation_word_si // Record whether this pause may need to trigger a concurrent operation. Later, // when we signal the G1ConcurrentMarkThread, the collector state has already // been reset for the next pause. - bool should_start_concurrent_mark_operation = collector_state()->in_concurrent_start_gc(); + bool should_start_concurrent_mark_operation = collector_state()->is_in_concurrent_start_gc(); // Perform the collection. G1YoungCollector collector(gc_cause(), allocation_word_size); @@ -2827,7 +2826,7 @@ bool G1STWSubjectToDiscoveryClosure::do_object_b(oop obj) { } void G1CollectedHeap::make_pending_list_reachable() { - if (collector_state()->in_concurrent_start_gc()) { + if (collector_state()->is_in_concurrent_start_gc()) { oop pll_head = Universe::reference_pending_list(); if (pll_head != nullptr) { // Any valid worker id is fine here as we are in the VM thread and single-threaded. @@ -3212,7 +3211,7 @@ void G1CollectedHeap::retire_gc_alloc_region(G1HeapRegion* alloc_region, _survivor.add_used_bytes(allocated_bytes); } - bool const during_im = collector_state()->in_concurrent_start_gc(); + bool const during_im = collector_state()->is_in_concurrent_start_gc(); if (during_im && allocated_bytes > 0) { _cm->add_root_region(alloc_region); } diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 7a4cde9001e..b5cb9167d92 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -34,7 +34,6 @@ #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1EdenRegions.hpp" #include "gc/g1/g1EvacStats.hpp" -#include "gc/g1/g1GCPauseType.hpp" #include "gc/g1/g1HeapRegionAttr.hpp" #include "gc/g1/g1HeapRegionManager.hpp" #include "gc/g1/g1HeapRegionSet.hpp" diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp index 8782b65b6f9..90e87607b87 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp @@ -47,9 +47,9 @@ #include "utilities/bitMap.inline.hpp" inline bool G1STWIsAliveClosure::do_object_b(oop p) { - // An object is reachable if it is outside the collection set, - // or is inside and copied. - return !_g1h->is_in_cset(p) || p->is_forwarded(); + // An object is reachable if it is outside the collection set and not a + // humongous candidate, or is inside and copied. + return !_g1h->is_in_cset_or_humongous_candidate(p) || p->is_forwarded(); } inline JavaThread* const* G1JavaThreadsListClaimer::claim(uint& count) { diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.cpp b/src/hotspot/share/gc/g1/g1CollectionSet.cpp index abfddf860e6..978925d88cb 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -174,7 +174,6 @@ void G1CollectionSet::iterate(G1HeapRegionClosure* cl) const { G1HeapRegion* r = _g1h->region_at(_regions[i]); bool result = cl->do_heap_region(r); if (result) { - cl->set_incomplete(); return; } } @@ -326,7 +325,7 @@ double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1Survi guarantee(target_pause_time_ms > 0.0, "target_pause_time_ms = %1.6lf should be positive", target_pause_time_ms); - bool in_young_only_phase = _policy->collector_state()->in_young_only_phase(); + bool in_young_only_phase = _policy->collector_state()->is_in_young_only_phase(); size_t pending_cards = _policy->analytics()->predict_pending_cards(in_young_only_phase); log_trace(gc, ergo, cset)("Start choosing CSet. Pending cards: %zu target pause time: %1.2fms", @@ -379,7 +378,7 @@ void G1CollectionSet::finalize_old_part(double time_remaining_ms) { if (!candidates()->is_empty()) { candidates()->verify(); - if (collector_state()->in_mixed_phase()) { + if (collector_state()->is_in_mixed_phase()) { time_remaining_ms = select_candidates_from_marking(time_remaining_ms); } else { log_debug(gc, ergo, cset)("Do not add marking candidates to collection set due to pause type."); diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp index d71108d4d0e..2113db1163b 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -23,7 +23,6 @@ */ #include "gc/g1/g1CollectionSetCandidates.inline.hpp" -#include "gc/g1/g1CollectionSetChooser.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "utilities/growableArray.hpp" @@ -250,8 +249,9 @@ void G1CollectionSetCandidates::sort_marking_by_efficiency() { _from_marking_groups.verify(); } -void G1CollectionSetCandidates::set_candidates_from_marking(G1HeapRegion** candidates, - uint num_candidates) { +void G1CollectionSetCandidates::set_candidates_from_marking(GrowableArrayCHeap* candidates) { + uint num_candidates = candidates->length(); + if (num_candidates == 0) { log_debug(gc, ergo, cset) ("No regions selected from marking."); return; @@ -264,7 +264,7 @@ void G1CollectionSetCandidates::set_candidates_from_marking(G1HeapRegion** candi // During each Mixed GC, we must collect at least G1Policy::calc_min_old_cset_length regions to meet // the G1MixedGCCountTarget. For the first collection in a Mixed GC cycle, we can add all regions // required to meet this threshold to the same remset group. We are certain these will be collected in - // the same MixedGC. + // the same Mixed GC. uint group_limit = p->calc_min_old_cset_length(num_candidates); G1CSetCandidateGroup::reset_next_group_id(); @@ -273,7 +273,7 @@ void G1CollectionSetCandidates::set_candidates_from_marking(G1HeapRegion** candi current = new G1CSetCandidateGroup(); for (uint i = 0; i < num_candidates; i++) { - G1HeapRegion* r = candidates[i]; + G1HeapRegion* r = candidates->at(i); assert(!contains(r), "must not contain region %u", r->hrm_index()); _contains_map[r->hrm_index()] = CandidateOrigin::Marking; diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp index 7e882de7e5a..8a2235cf89c 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -245,8 +245,7 @@ public: // Merge collection set candidates from marking into the current marking candidates // (which needs to be empty). - void set_candidates_from_marking(G1HeapRegion** candidates, - uint num_candidates); + void set_candidates_from_marking(GrowableArrayCHeap* selected); // The most recent length of the list that had been merged last via // set_candidates_from_marking(). Used for calculating minimum collection set // regions. diff --git a/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp b/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp deleted file mode 100644 index e7bab32129e..00000000000 --- a/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright (c) 2001, 2026, 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. - * - */ - -#include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1CollectionSetCandidates.hpp" -#include "gc/g1/g1CollectionSetChooser.hpp" -#include "gc/g1/g1HeapRegionRemSet.inline.hpp" -#include "gc/shared/space.hpp" -#include "runtime/atomic.hpp" -#include "utilities/quickSort.hpp" - -// Determine collection set candidates (from marking): For all regions determine -// whether they should be a collection set candidate. Calculate their efficiency, -// sort, and put them into the collection set candidates. -// -// Threads calculate the GC efficiency of the regions they get to process, and -// put them into some work area without sorting. At the end that array is sorted and -// moved to the destination. -class G1BuildCandidateRegionsTask : public WorkerTask { - // Work area for building the set of collection set candidates. Contains references - // to heap regions with their GC efficiencies calculated. To reduce contention - // on claiming array elements, worker threads claim parts of this array in chunks; - // Array elements may be null as threads might not get enough regions to fill - // up their chunks completely. - // Final sorting will remove them. - class G1BuildCandidateArray : public StackObj { - uint const _max_size; - uint const _chunk_size; - - G1HeapRegion** _data; - - Atomic _cur_claim_idx; - - static int compare_region_gc_efficiency(G1HeapRegion** rr1, G1HeapRegion** rr2) { - G1HeapRegion* r1 = *rr1; - G1HeapRegion* r2 = *rr2; - // Make sure that null entries are moved to the end. - if (r1 == nullptr) { - if (r2 == nullptr) { - return 0; - } else { - return 1; - } - } else if (r2 == nullptr) { - return -1; - } - - G1Policy* p = G1CollectedHeap::heap()->policy(); - double gc_efficiency1 = p->predict_gc_efficiency(r1); - double gc_efficiency2 = p->predict_gc_efficiency(r2); - - if (gc_efficiency1 > gc_efficiency2) { - return -1; - } else if (gc_efficiency1 < gc_efficiency2) { - return 1; - } else { - return 0; - } - } - - // Calculates the maximum array size that will be used. - static uint required_array_size(uint num_regions, uint chunk_size, uint num_workers) { - uint const max_waste = num_workers * chunk_size; - // The array should be aligned with respect to chunk_size. - uint const aligned_num_regions = ((num_regions + chunk_size - 1) / chunk_size) * chunk_size; - - return aligned_num_regions + max_waste; - } - - public: - G1BuildCandidateArray(uint max_num_regions, uint chunk_size, uint num_workers) : - _max_size(required_array_size(max_num_regions, chunk_size, num_workers)), - _chunk_size(chunk_size), - _data(NEW_C_HEAP_ARRAY(G1HeapRegion*, _max_size, mtGC)), - _cur_claim_idx(0) { - for (uint i = 0; i < _max_size; i++) { - _data[i] = nullptr; - } - } - - ~G1BuildCandidateArray() { - FREE_C_HEAP_ARRAY(G1HeapRegion*, _data); - } - - // Claim a new chunk, returning its bounds [from, to[. - void claim_chunk(uint& from, uint& to) { - uint result = _cur_claim_idx.add_then_fetch(_chunk_size); - assert(_max_size > result - 1, - "Array too small, is %u should be %u with chunk size %u.", - _max_size, result, _chunk_size); - from = result - _chunk_size; - to = result; - } - - // Set element in array. - void set(uint idx, G1HeapRegion* hr) { - assert(idx < _max_size, "Index %u out of bounds %u", idx, _max_size); - assert(_data[idx] == nullptr, "Value must not have been set."); - _data[idx] = hr; - } - - void sort_by_gc_efficiency() { - uint length = _cur_claim_idx.load_relaxed(); - if (length == 0) { - return; - } - for (uint i = length; i < _max_size; i++) { - assert(_data[i] == nullptr, "must be"); - } - qsort(_data, length, sizeof(_data[0]), (_sort_Fn)compare_region_gc_efficiency); - for (uint i = length; i < _max_size; i++) { - assert(_data[i] == nullptr, "must be"); - } - } - - G1HeapRegion** array() const { return _data; } - }; - - // Per-region closure. In addition to determining whether a region should be - // added to the candidates, and calculating those regions' gc efficiencies, also - // gather additional statistics. - class G1BuildCandidateRegionsClosure : public G1HeapRegionClosure { - G1BuildCandidateArray* _array; - - uint _cur_chunk_idx; - uint _cur_chunk_end; - - uint _regions_added; - - void add_region(G1HeapRegion* hr) { - if (_cur_chunk_idx == _cur_chunk_end) { - _array->claim_chunk(_cur_chunk_idx, _cur_chunk_end); - } - assert(_cur_chunk_idx < _cur_chunk_end, "Must be"); - - _array->set(_cur_chunk_idx, hr); - _cur_chunk_idx++; - - _regions_added++; - } - - public: - G1BuildCandidateRegionsClosure(G1BuildCandidateArray* array) : - _array(array), - _cur_chunk_idx(0), - _cur_chunk_end(0), - _regions_added(0) { } - - bool do_heap_region(G1HeapRegion* r) { - // Candidates from marking are always old; also keep regions that are already - // collection set candidates (some retained regions) in that list. - if (!r->is_old() || r->is_collection_set_candidate()) { - // Keep remembered sets and everything for these regions. - return false; - } - - // Can not add a region without a remembered set to the candidates. - if (!r->rem_set()->is_tracked()) { - return false; - } - - // Skip any region that is currently used as an old GC alloc region. We should - // not consider those for collection before we fill them up as the effective - // gain from them is small. I.e. we only actually reclaim from the filled part, - // as the remainder is still eligible for allocation. These objects are also - // likely to have already survived a few collections, so they might be longer - // lived anyway. - // Otherwise the Old region must satisfy the liveness condition. - bool should_add = !G1CollectedHeap::heap()->is_old_gc_alloc_region(r) && - G1CollectionSetChooser::region_occupancy_low_enough_for_evac(r->live_bytes()); - if (should_add) { - add_region(r); - } else { - r->rem_set()->clear(true /* only_cardset */); - } - return false; - } - - uint regions_added() const { return _regions_added; } - }; - - G1CollectedHeap* _g1h; - G1HeapRegionClaimer _hrclaimer; - - Atomic _num_regions_added; - - G1BuildCandidateArray _result; - - void update_totals(uint num_regions) { - if (num_regions > 0) { - _num_regions_added.add_then_fetch(num_regions); - } - } - - // Early prune (remove) regions meeting the G1HeapWastePercent criteria. That - // is, either until only the minimum amount of old collection set regions are - // available (for forward progress in evacuation) or the waste accumulated by the - // removed regions is above the maximum allowed waste. - // Updates number of candidates and reclaimable bytes given. - void prune(G1HeapRegion** data) { - G1Policy* p = G1CollectedHeap::heap()->policy(); - - uint num_candidates = _num_regions_added.load_relaxed(); - - uint min_old_cset_length = p->calc_min_old_cset_length(num_candidates); - uint num_pruned = 0; - size_t wasted_bytes = 0; - - if (min_old_cset_length >= num_candidates) { - // We take all of the candidate regions to provide some forward progress. - return; - } - - size_t allowed_waste = p->allowed_waste_in_collection_set(); - uint max_to_prune = num_candidates - min_old_cset_length; - - while (true) { - G1HeapRegion* r = data[num_candidates - num_pruned - 1]; - size_t const reclaimable = r->reclaimable_bytes(); - if (num_pruned >= max_to_prune || - wasted_bytes + reclaimable > allowed_waste) { - break; - } - r->rem_set()->clear(true /* cardset_only */); - - wasted_bytes += reclaimable; - num_pruned++; - } - - log_debug(gc, ergo, cset)("Pruned %u regions out of %u, leaving %zu bytes waste (allowed %zu)", - num_pruned, - num_candidates, - wasted_bytes, - allowed_waste); - - _num_regions_added.sub_then_fetch(num_pruned, memory_order_relaxed); - } - -public: - G1BuildCandidateRegionsTask(uint max_num_regions, uint chunk_size, uint num_workers) : - WorkerTask("G1 Build Candidate Regions"), - _g1h(G1CollectedHeap::heap()), - _hrclaimer(num_workers), - _num_regions_added(0), - _result(max_num_regions, chunk_size, num_workers) { } - - void work(uint worker_id) { - G1BuildCandidateRegionsClosure cl(&_result); - _g1h->heap_region_par_iterate_from_worker_offset(&cl, &_hrclaimer, worker_id); - update_totals(cl.regions_added()); - } - - void sort_and_prune_into(G1CollectionSetCandidates* candidates) { - _result.sort_by_gc_efficiency(); - prune(_result.array()); - candidates->set_candidates_from_marking(_result.array(), - _num_regions_added.load_relaxed()); - } -}; - -uint G1CollectionSetChooser::calculate_work_chunk_size(uint num_workers, uint num_regions) { - assert(num_workers > 0, "Active gc workers should be greater than 0"); - return MAX2(num_regions / num_workers, 1U); -} - -void G1CollectionSetChooser::build(WorkerThreads* workers, uint max_num_regions, G1CollectionSetCandidates* candidates) { - uint num_workers = workers->active_workers(); - uint chunk_size = calculate_work_chunk_size(num_workers, max_num_regions); - - G1BuildCandidateRegionsTask cl(max_num_regions, chunk_size, num_workers); - workers->run_task(&cl, num_workers); - - cl.sort_and_prune_into(candidates); - candidates->verify(); -} diff --git a/src/hotspot/share/gc/g1/g1CollectionSetChooser.hpp b/src/hotspot/share/gc/g1/g1CollectionSetChooser.hpp deleted file mode 100644 index 4db8ed23c49..00000000000 --- a/src/hotspot/share/gc/g1/g1CollectionSetChooser.hpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2001, 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. - * - * 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. - * - */ - -#ifndef SHARE_GC_G1_G1COLLECTIONSETCHOOSER_HPP -#define SHARE_GC_G1_G1COLLECTIONSETCHOOSER_HPP - -#include "gc/g1/g1HeapRegion.hpp" -#include "gc/shared/gc_globals.hpp" -#include "memory/allStatic.hpp" -#include "runtime/globals.hpp" - -class G1CollectionSetCandidates; -class WorkerThreads; - -// Helper class to calculate collection set candidates, and containing some related -// methods. -class G1CollectionSetChooser : public AllStatic { - static uint calculate_work_chunk_size(uint num_workers, uint num_regions); - - static size_t mixed_gc_live_threshold_bytes() { - return G1HeapRegion::GrainBytes * (size_t)G1MixedGCLiveThresholdPercent / 100; - } - -public: - static bool region_occupancy_low_enough_for_evac(size_t live_bytes) { - return live_bytes < mixed_gc_live_threshold_bytes(); - } - - // Build and return set of collection set candidates sorted by decreasing gc - // efficiency. - static void build(WorkerThreads* workers, uint max_num_regions, G1CollectionSetCandidates* candidates); -}; - -#endif // SHARE_GC_G1_G1COLLECTIONSETCHOOSER_HPP diff --git a/src/hotspot/share/gc/g1/g1CollectorState.cpp b/src/hotspot/share/gc/g1/g1CollectorState.cpp index d41ee22fdce..66292642603 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.cpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -22,25 +22,42 @@ * */ +#include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectorState.hpp" -#include "gc/g1/g1GCPauseType.hpp" +#include "gc/g1/g1ConcurrentMarkThread.inline.hpp" +#include "runtime/safepoint.hpp" -G1GCPauseType G1CollectorState::young_gc_pause_type(bool concurrent_operation_is_full_mark) const { - assert(!in_full_gc(), "must be"); - if (in_concurrent_start_gc()) { - assert(!in_young_gc_before_mixed(), "must be"); - return concurrent_operation_is_full_mark ? G1GCPauseType::ConcurrentStartMarkGC : - G1GCPauseType::ConcurrentStartUndoGC; - } else if (in_young_gc_before_mixed()) { - assert(!in_concurrent_start_gc(), "must be"); - return G1GCPauseType::LastYoungGC; - } else if (in_mixed_phase()) { - assert(!in_concurrent_start_gc(), "must be"); - assert(!in_young_gc_before_mixed(), "must be"); - return G1GCPauseType::MixedGC; - } else { - assert(!in_concurrent_start_gc(), "must be"); - assert(!in_young_gc_before_mixed(), "must be"); - return G1GCPauseType::YoungGC; +G1CollectorState::Pause G1CollectorState::gc_pause_type(bool concurrent_operation_is_full_mark) const { + assert(SafepointSynchronize::is_at_safepoint(), "must be"); + switch (_phase) { + case Phase::YoungNormal: return Pause::Normal; + case Phase::YoungLastYoung: return Pause::LastYoung; + case Phase::YoungConcurrentStart: + return concurrent_operation_is_full_mark ? Pause::ConcurrentStartFull : + Pause::ConcurrentStartUndo; + case Phase::Mixed: return Pause::Mixed; + case Phase::FullGC: return Pause::Full; + default: ShouldNotReachHere(); } } + +bool G1CollectorState::is_in_concurrent_cycle() const { + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + return cm->is_in_concurrent_cycle(); +} + +bool G1CollectorState::is_in_marking() const { + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + return cm->is_in_marking(); +} + +bool G1CollectorState::is_in_mark_or_rebuild() const { + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + return is_in_marking() || cm->is_in_rebuild_or_scrub(); +} + +bool G1CollectorState::is_in_reset_for_next_cycle() const { + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + return cm->is_in_reset_for_next_cycle(); +} + diff --git a/src/hotspot/share/gc/g1/g1CollectorState.hpp b/src/hotspot/share/gc/g1/g1CollectorState.hpp index fca30792344..fc59df7349d 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.hpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -25,98 +25,134 @@ #ifndef SHARE_GC_G1_G1COLLECTORSTATE_HPP #define SHARE_GC_G1_G1COLLECTORSTATE_HPP -#include "gc/g1/g1GCPauseType.hpp" +#include "utilities/debug.hpp" +#include "utilities/enumIterator.hpp" #include "utilities/globalDefinitions.hpp" // State of the G1 collection. +// +// The rough phasing is Young-Only, Mixed / Space Reclamation and +// Full GC "phase". +// +// We split the Young-only phase into three parts to cover interesting +// sub-phases and avoid separate tracking. class G1CollectorState { - // Indicates whether we are in the phase where we do partial gcs that only contain - // the young generation. Not set while _in_full_gc is set. - bool _in_young_only_phase; + enum class Phase { + // Indicates that the next GC in the Young-Only phase will (likely) be a "Normal" + // young GC. + YoungNormal, + // We are in a concurrent start GC during the Young-Only phase. This is only set + // during that GC because we only decide whether we do this type of GC at the start + // of the pause. + YoungConcurrentStart, + // Indicates that we are about to start or in the last young gc in the Young-Only + // phase before the Mixed phase. This GC is required to keep pause time requirements. + YoungLastYoung, + // Doing extra old generation evacuation. + Mixed, + // The Full GC phase (that coincides with the Full GC pause). + FullGC + } _phase; - // Indicates whether we are in the last young gc before the mixed gc phase. This GC - // is required to keep pause time requirements. - bool _in_young_gc_before_mixed; - - // If _initiate_conc_mark_if_possible is set at the beginning of a - // pause, it is a suggestion that the pause should start a marking - // cycle by doing the concurrent start work. However, it is possible - // that the concurrent marking thread is still finishing up the - // previous marking cycle (e.g., clearing the marking bitmap). - // If that is the case we cannot start a new cycle and - // we'll have to wait for the concurrent marking thread to finish - // what it is doing. In this case we will postpone the marking cycle - // initiation decision for the next pause. When we eventually decide - // to start a cycle, we will set _in_concurrent_start_gc which - // will stay true until the end of the concurrent start pause doing the - // concurrent start work. - volatile bool _in_concurrent_start_gc; - - // At the end of a pause we check the heap occupancy and we decide - // whether we will start a marking cycle during the next pause. If - // we decide that we want to do that, set this parameter. This parameter will - // stay set until the beginning of a subsequent pause (not necessarily - // the next one) when we decide that we will indeed start a marking cycle and - // do the concurrent start phase work. + // _initiate_conc_mark_if_possible indicates that there has been a request to start + // a concurrent cycle but we have not been able to fulfill it because another one + // has been in progress when the request came in. + // + // This flag remembers that there is an unfullfilled request. volatile bool _initiate_conc_mark_if_possible; - // Marking is in progress. Set from start of the concurrent start pause to the - // end of the Remark pause. - bool _mark_in_progress; - // Marking or rebuilding remembered set work is in progress. Set from the end - // of the concurrent start pause to the end of the Cleanup pause. - bool _mark_or_rebuild_in_progress; - - // The marking bitmap is currently being cleared or about to be cleared. - bool _clear_bitmap_in_progress; - - // Set during a full gc pause. - bool _in_full_gc; - public: G1CollectorState() : - _in_young_only_phase(true), - _in_young_gc_before_mixed(false), - - _in_concurrent_start_gc(false), - _initiate_conc_mark_if_possible(false), - - _mark_in_progress(false), - _mark_or_rebuild_in_progress(false), - _clear_bitmap_in_progress(false), - _in_full_gc(false) { } + _phase(Phase::YoungNormal), + _initiate_conc_mark_if_possible(false) { } // Phase setters - void set_in_young_only_phase(bool v) { _in_young_only_phase = v; } + void set_in_normal_young_gc() { _phase = Phase::YoungNormal; } + void set_in_space_reclamation_phase() { _phase = Phase::Mixed; } + void set_in_full_gc() { _phase = Phase::FullGC; } // Pause setters - void set_in_young_gc_before_mixed(bool v) { _in_young_gc_before_mixed = v; } - void set_in_concurrent_start_gc(bool v) { _in_concurrent_start_gc = v; } - void set_in_full_gc(bool v) { _in_full_gc = v; } + void set_in_young_gc_before_mixed() { _phase = Phase::YoungLastYoung; } + void set_in_concurrent_start_gc() { _phase = Phase::YoungConcurrentStart; _initiate_conc_mark_if_possible = false; } void set_initiate_conc_mark_if_possible(bool v) { _initiate_conc_mark_if_possible = v; } - void set_mark_in_progress(bool v) { _mark_in_progress = v; } - void set_mark_or_rebuild_in_progress(bool v) { _mark_or_rebuild_in_progress = v; } - void set_clear_bitmap_in_progress(bool v) { _clear_bitmap_in_progress = v; } - // Phase getters - bool in_young_only_phase() const { return _in_young_only_phase && !_in_full_gc; } - bool in_mixed_phase() const { return !_in_young_only_phase && !_in_full_gc; } + bool is_in_young_only_phase() const { return _phase == Phase::YoungNormal || _phase == Phase::YoungConcurrentStart || _phase == Phase::YoungLastYoung; } + bool is_in_mixed_phase() const { return _phase == Phase::Mixed; } // Specific pauses - bool in_young_gc_before_mixed() const { return _in_young_gc_before_mixed; } - bool in_full_gc() const { return _in_full_gc; } - bool in_concurrent_start_gc() const { return _in_concurrent_start_gc; } + bool is_in_young_gc_before_mixed() const { return _phase == Phase::YoungLastYoung; } + bool is_in_full_gc() const { return _phase == Phase::FullGC; } + bool is_in_concurrent_start_gc() const { return _phase == Phase::YoungConcurrentStart; } bool initiate_conc_mark_if_possible() const { return _initiate_conc_mark_if_possible; } - bool mark_in_progress() const { return _mark_in_progress; } - bool mark_or_rebuild_in_progress() const { return _mark_or_rebuild_in_progress; } - bool clear_bitmap_in_progress() const { return _clear_bitmap_in_progress; } + bool is_in_concurrent_cycle() const; + bool is_in_marking() const; + bool is_in_mark_or_rebuild() const; + bool is_in_reset_for_next_cycle() const; + + enum class Pause : uint { + Normal, + LastYoung, + ConcurrentStartFull, + ConcurrentStartUndo, + Cleanup, + Remark, + Mixed, + Full + }; // Calculate GC Pause Type from internal state. - G1GCPauseType young_gc_pause_type(bool concurrent_operation_is_full_mark) const; + Pause gc_pause_type(bool concurrent_operation_is_full_mark) const; + + static const char* to_string(Pause type) { + static const char* pause_strings[] = { "Normal", + "Prepare Mixed", + "Concurrent Start", // Do not distinguish between the different + "Concurrent Start", // Concurrent Start pauses. + "Cleanup", + "Remark", + "Mixed", + "Full" }; + return pause_strings[static_cast(type)]; + } + + static void assert_is_young_pause(Pause type) { + assert(type != Pause::Full, "must be"); + assert(type != Pause::Remark, "must be"); + assert(type != Pause::Cleanup, "must be"); + } + + static bool is_young_only_pause(Pause type) { + assert_is_young_pause(type); + return type == Pause::ConcurrentStartUndo || + type == Pause::ConcurrentStartFull || + type == Pause::LastYoung || + type == Pause::Normal; + } + + static bool is_mixed_pause(Pause type) { + assert_is_young_pause(type); + return type == Pause::Mixed; + } + + static bool is_last_young_pause(Pause type) { + assert_is_young_pause(type); + return type == Pause::LastYoung; + } + + static bool is_concurrent_start_pause(Pause type) { + assert_is_young_pause(type); + return type == Pause::ConcurrentStartFull || type == Pause::ConcurrentStartUndo; + } + + static bool is_concurrent_cycle_pause(Pause type) { + return type == Pause::Cleanup || type == Pause::Remark; + } }; +ENUMERATOR_RANGE(G1CollectorState::Pause, G1CollectorState::Pause::Normal, G1CollectorState::Pause::Full) + #endif // SHARE_GC_G1_G1COLLECTORSTATE_HPP diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index b4a5d673fd2..8fd355615d0 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -30,7 +30,6 @@ #include "gc/g1/g1CardSetMemory.hpp" #include "gc/g1/g1CardTableClaimTable.inline.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1CollectionSetChooser.hpp" #include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1ConcurrentMark.inline.hpp" #include "gc/g1/g1ConcurrentMarkRemarkTasks.hpp" @@ -573,8 +572,20 @@ void G1ConcurrentMark::fully_initialize() { reset_at_marking_complete(); } -bool G1ConcurrentMark::in_progress() const { - return is_fully_initialized() ? _cm_thread->in_progress() : false; +bool G1ConcurrentMark::is_in_concurrent_cycle() const { + return is_fully_initialized() ? _cm_thread->is_in_progress() : false; +} + +bool G1ConcurrentMark::is_in_marking() const { + return is_fully_initialized() ? cm_thread()->is_in_marking() : false; +} + +bool G1ConcurrentMark::is_in_rebuild_or_scrub() const { + return cm_thread()->is_in_rebuild_or_scrub(); +} + +bool G1ConcurrentMark::is_in_reset_for_next_cycle() const { + return cm_thread()->is_in_reset_for_next_cycle(); } PartialArrayStateManager* G1ConcurrentMark::partial_array_state_manager() const { @@ -622,7 +633,7 @@ void G1ConcurrentMark::humongous_object_eagerly_reclaimed(G1HeapRegion* r) { // Need to clear mark bit of the humongous object. Doing this unconditionally is fine. mark_bitmap()->clear(r->bottom()); - if (!_g1h->collector_state()->mark_or_rebuild_in_progress()) { + if (!_g1h->collector_state()->is_in_mark_or_rebuild()) { return; } @@ -682,14 +693,14 @@ void G1ConcurrentMark::set_concurrency_and_phase(uint active_tasks, bool concurr #if TASKQUEUE_STATS void G1ConcurrentMark::print_and_reset_taskqueue_stats() { - _task_queues->print_and_reset_taskqueue_stats("G1ConcurrentMark Oop Queue"); + _task_queues->print_and_reset_taskqueue_stats("Concurrent Mark"); auto get_pa_stats = [&](uint i) { return _tasks[i]->partial_array_task_stats(); }; PartialArrayTaskStats::log_set(_max_num_tasks, get_pa_stats, - "G1ConcurrentMark Partial Array Task Stats"); + "Concurrent Mark Partial Array"); for (uint i = 0; i < _max_num_tasks; ++i) { get_pa_stats(i)->reset(); @@ -729,7 +740,7 @@ private: } bool is_clear_concurrent_undo() { - return suspendible() && _cm->cm_thread()->in_undo_mark(); + return suspendible() && _cm->cm_thread()->is_in_undo_cycle(); } bool has_aborted() { @@ -785,8 +796,7 @@ private: // as asserts here to minimize their overhead on the product. However, we // will have them as guarantees at the beginning / end of the bitmap // clearing to get some checking in the product. - assert(!suspendible() || _cm->in_progress(), "invariant"); - assert(!suspendible() || !G1CollectedHeap::heap()->collector_state()->mark_or_rebuild_in_progress(), "invariant"); + assert(!suspendible() || _cm->is_in_reset_for_next_cycle(), "invariant"); // Abort iteration if necessary. if (has_aborted()) { @@ -817,10 +827,6 @@ public: SuspendibleThreadSetJoiner sts_join(_suspendible); G1CollectedHeap::heap()->heap_region_par_iterate_from_worker_offset(&_cl, &_hr_claimer, worker_id); } - - bool is_complete() { - return _cl.is_complete(); - } }; void G1ConcurrentMark::clear_bitmap(WorkerThreads* workers, bool may_yield) { @@ -835,29 +841,19 @@ void G1ConcurrentMark::clear_bitmap(WorkerThreads* workers, bool may_yield) { log_debug(gc, ergo)("Running %s with %u workers for %zu work units.", cl.name(), num_workers, num_chunks); workers->run_task(&cl, num_workers); - guarantee(may_yield || cl.is_complete(), "Must have completed iteration when not yielding."); } void G1ConcurrentMark::cleanup_for_next_mark() { // Make sure that the concurrent mark thread looks to still be in // the current cycle. - guarantee(is_fully_initialized(), "should be initializd"); - guarantee(in_progress(), "invariant"); - - // We are finishing up the current cycle by clearing the next - // marking bitmap and getting it ready for the next cycle. During - // this time no other cycle can start. So, let's make sure that this - // is the case. - guarantee(!_g1h->collector_state()->mark_or_rebuild_in_progress(), "invariant"); + guarantee(is_in_reset_for_next_cycle(), "invariant"); clear_bitmap(_concurrent_workers, true); reset_partial_array_state_manager(); - // Repeat the asserts from above. - guarantee(is_fully_initialized(), "should be initializd"); - guarantee(in_progress(), "invariant"); - guarantee(!_g1h->collector_state()->mark_or_rebuild_in_progress(), "invariant"); + // Should not have changed state yet (even if a Full GC interrupted us). + guarantee(is_in_reset_for_next_cycle(), "invariant"); } void G1ConcurrentMark::reset_partial_array_state_manager() { @@ -982,14 +978,14 @@ void G1ConcurrentMark::start_full_concurrent_cycle() { // during it. No need to call it here. // Signal the thread to start work. - cm_thread()->start_full_mark(); + cm_thread()->start_full_cycle(); } void G1ConcurrentMark::start_undo_concurrent_cycle() { root_regions()->cancel_scan(); // Signal the thread to start work. - cm_thread()->start_undo_mark(); + cm_thread()->start_undo_cycle(); } void G1ConcurrentMark::notify_concurrent_cycle_completed() { @@ -1191,8 +1187,6 @@ uint G1ConcurrentMark::completed_mark_cycles() const { } void G1ConcurrentMark::concurrent_cycle_end(bool mark_cycle_completed) { - _g1h->collector_state()->set_clear_bitmap_in_progress(false); - _g1h->trace_heap_after_gc(_gc_tracer_cm); if (mark_cycle_completed) { @@ -1333,14 +1327,13 @@ void G1ConcurrentMark::remark() { _g1h->workers()->run_task(&cl, num_workers); log_debug(gc, remset, tracking)("Remembered Set Tracking update regions total %u, selected %u", - _g1h->num_committed_regions(), cl.total_selected_for_rebuild()); + _g1h->num_committed_regions(), cl.total_selected_for_rebuild()); _needs_remembered_set_rebuild = (cl.total_selected_for_rebuild() > 0); if (_needs_remembered_set_rebuild) { - // Prune rebuild candidates based on G1HeapWastePercent. - // Improves rebuild time in addition to remembered set memory usage. - G1CollectionSetChooser::build(_g1h->workers(), _g1h->num_committed_regions(), _g1h->policy()->candidates()); + GrowableArrayCHeap* selected = cl.sort_and_prune_old_selected(); + _g1h->policy()->candidates()->set_candidates_from_marking(selected); } } @@ -1377,6 +1370,9 @@ void G1ConcurrentMark::remark() { G1ObjectCountIsAliveClosure is_alive(_g1h); _gc_tracer_cm->report_object_count_after_gc(&is_alive, _g1h->workers()); } + + // Successfully completed marking, advance state. + cm_thread()->set_full_cycle_rebuild_and_scrub(); } else { // We overflowed. Restart concurrent marking. _restart_for_overflow.store_relaxed(true); @@ -1397,6 +1393,8 @@ void G1ConcurrentMark::remark() { _g1h->update_perf_counter_cpu_time(); policy->record_concurrent_mark_remark_end(); + + return; } void G1ConcurrentMark::compute_new_sizes() { @@ -1459,6 +1457,10 @@ void G1ConcurrentMark::cleanup() { GCTraceTime(Debug, gc, phases) debug("Finalize Concurrent Mark Cleanup", _gc_timer_cm); policy->record_concurrent_mark_cleanup_end(needs_remembered_set_rebuild()); } + + // Advance state. + cm_thread()->set_full_cycle_reset_for_next_cycle(); + return; } // 'Keep Alive' oop closure used by both serial parallel reference processing. @@ -1885,7 +1887,7 @@ public: void G1ConcurrentMark::verify_no_collection_set_oops() { assert(SafepointSynchronize::is_at_safepoint() || !is_init_completed(), "should be at a safepoint or initializing"); - if (!_g1h->collector_state()->mark_or_rebuild_in_progress()) { + if (!is_fully_initialized() || !_g1h->collector_state()->is_in_mark_or_rebuild()) { return; } @@ -1963,7 +1965,7 @@ bool G1ConcurrentMark::concurrent_cycle_abort() { // has been signalled is already rare), and this work should be negligible compared // to actual full gc work. - if (!is_fully_initialized() || (!cm_thread()->in_progress() && !cm_thread()->should_terminate())) { + if (!is_fully_initialized() || (!cm_thread()->is_in_progress() && !cm_thread()->should_terminate())) { return false; } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 8bd04437097..de97179d210 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -353,6 +353,7 @@ class G1ConcurrentMark : public CHeapObj { friend class G1CMRootRegionScanTask; friend class G1CMTask; friend class G1ClearBitMapTask; + friend class G1CollectorState; friend class G1ConcurrentMarkThread; G1ConcurrentMarkThread* _cm_thread; // The thread doing the work @@ -528,6 +529,12 @@ class G1ConcurrentMark : public CHeapObj { G1ConcurrentMarkThread* cm_thread() const; + // Concurrent cycle state queries. + bool is_in_concurrent_cycle() const; + bool is_in_marking() const; + bool is_in_rebuild_or_scrub() const; + bool is_in_reset_for_next_cycle() const; + public: // To be called when an object is marked the first time, e.g. after a successful // mark_in_bitmap call. Updates various statistics data. @@ -561,7 +568,7 @@ public: void fully_initialize(); bool is_fully_initialized() const { return _cm_thread != nullptr; } - bool in_progress() const; + uint max_num_tasks() const {return _max_num_tasks; } // Clear statistics gathered during the concurrent cycle for the given region after @@ -666,8 +673,10 @@ public: // Do concurrent preclean work. void preclean(); + // Executes the Remark pause. void remark(); + // Executes the Cleanup pause. void cleanup(); // Mark in the marking bitmap. Used during evacuation failure to diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.cpp index 4eb11f6d8f6..3eda7200e25 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.cpp @@ -31,28 +31,32 @@ #include "gc/g1/g1RemSetTrackingPolicy.hpp" #include "logging/log.hpp" #include "runtime/mutexLocker.hpp" +#include "utilities/growableArray.hpp" struct G1UpdateRegionLivenessAndSelectForRebuildTask::G1OnRegionClosure : public G1HeapRegionClosure { G1CollectedHeap* _g1h; G1ConcurrentMark* _cm; - // The number of regions actually selected for rebuild. - uint _num_selected_for_rebuild; size_t _freed_bytes; uint _num_old_regions_removed; uint _num_humongous_regions_removed; - G1FreeRegionList* _local_cleanup_list; + + GrowableArrayCHeap _old_selected_for_rebuild; + uint _num_humongous_selected_for_rebuild; + + G1FreeRegionList* _cleanup_list; G1OnRegionClosure(G1CollectedHeap* g1h, G1ConcurrentMark* cm, G1FreeRegionList* local_cleanup_list) : _g1h(g1h), _cm(cm), - _num_selected_for_rebuild(0), _freed_bytes(0), _num_old_regions_removed(0), _num_humongous_regions_removed(0), - _local_cleanup_list(local_cleanup_list) {} + _old_selected_for_rebuild(16), + _num_humongous_selected_for_rebuild(0), + _cleanup_list(local_cleanup_list) {} void reclaim_empty_region_common(G1HeapRegion* hr) { assert(!hr->has_pinned_objects(), "precondition"); @@ -74,7 +78,7 @@ struct G1UpdateRegionLivenessAndSelectForRebuildTask::G1OnRegionClosure : public _num_humongous_regions_removed++; reclaim_empty_region_common(hr); - _g1h->free_humongous_region(hr, _local_cleanup_list); + _g1h->free_humongous_region(hr, _cleanup_list); }; _g1h->humongous_obj_regions_iterate(hr, on_humongous_region); @@ -85,7 +89,7 @@ struct G1UpdateRegionLivenessAndSelectForRebuildTask::G1OnRegionClosure : public _num_old_regions_removed++; reclaim_empty_region_common(hr); - _g1h->free_region(hr, _local_cleanup_list); + _g1h->free_region(hr, _cleanup_list); } bool do_heap_region(G1HeapRegion* hr) override { @@ -98,13 +102,13 @@ struct G1UpdateRegionLivenessAndSelectForRebuildTask::G1OnRegionClosure : public || hr->has_pinned_objects(); if (is_live) { const bool selected_for_rebuild = tracker->update_humongous_before_rebuild(hr); + auto on_humongous_region = [&] (G1HeapRegion* hr) { if (selected_for_rebuild) { - _num_selected_for_rebuild++; + _num_humongous_selected_for_rebuild++; } _cm->update_top_at_rebuild_start(hr); }; - _g1h->humongous_obj_regions_iterate(hr, on_humongous_region); } else { reclaim_empty_humongous_region(hr); @@ -118,7 +122,7 @@ struct G1UpdateRegionLivenessAndSelectForRebuildTask::G1OnRegionClosure : public if (is_live) { const bool selected_for_rebuild = tracker->update_old_before_rebuild(hr); if (selected_for_rebuild) { - _num_selected_for_rebuild++; + _old_selected_for_rebuild.push(hr); } _cm->update_top_at_rebuild_start(hr); } else { @@ -137,7 +141,8 @@ G1UpdateRegionLivenessAndSelectForRebuildTask::G1UpdateRegionLivenessAndSelectFo _g1h(g1h), _cm(cm), _hrclaimer(num_workers), - _total_selected_for_rebuild(0), + _old_selected_for_rebuild(128), + _num_humongous_selected_for_rebuild(0), _cleanup_list("Empty Regions After Mark List") {} G1UpdateRegionLivenessAndSelectForRebuildTask::~G1UpdateRegionLivenessAndSelectForRebuildTask() { @@ -153,8 +158,6 @@ void G1UpdateRegionLivenessAndSelectForRebuildTask::work(uint worker_id) { G1OnRegionClosure on_region_cl(_g1h, _cm, &local_cleanup_list); _g1h->heap_region_par_iterate_from_worker_offset(&on_region_cl, &_hrclaimer, worker_id); - _total_selected_for_rebuild.add_then_fetch(on_region_cl._num_selected_for_rebuild); - // Update the old/humongous region sets _g1h->remove_from_old_gen_sets(on_region_cl._num_old_regions_removed, on_region_cl._num_humongous_regions_removed); @@ -163,6 +166,9 @@ void G1UpdateRegionLivenessAndSelectForRebuildTask::work(uint worker_id) { MutexLocker x(G1RareEvent_lock, Mutex::_no_safepoint_check_flag); _g1h->decrement_summary_bytes(on_region_cl._freed_bytes); + _old_selected_for_rebuild.appendAll(&on_region_cl._old_selected_for_rebuild); + _num_humongous_selected_for_rebuild += on_region_cl._num_humongous_selected_for_rebuild; + _cleanup_list.add_ordered(&local_cleanup_list); assert(local_cleanup_list.is_empty(), "post-condition"); } @@ -172,3 +178,78 @@ uint G1UpdateRegionLivenessAndSelectForRebuildTask::desired_num_workers(uint num const uint num_regions_per_worker = 384; return (num_regions + num_regions_per_worker - 1) / num_regions_per_worker; } + +// Early prune (remove) regions meeting the G1HeapWastePercent criteria. That +// is, either until only the minimum amount of old collection set regions are +// available (for forward progress in evacuation) or the waste accumulated by the +// removed regions is above the maximum allowed waste. +// Updates number of candidates and reclaimable bytes given. +void G1UpdateRegionLivenessAndSelectForRebuildTask::prune(GrowableArrayCHeap* old_regions) { + G1Policy* p = G1CollectedHeap::heap()->policy(); + + uint num_candidates = (uint)old_regions->length(); + + uint min_old_cset_length = p->calc_min_old_cset_length(num_candidates); + uint num_pruned = 0; + size_t wasted_bytes = 0; + + if (min_old_cset_length >= num_candidates) { + // We take all of the candidate regions to provide some forward progress. + return; + } + + size_t allowed_waste = p->allowed_waste_in_collection_set(); + uint max_to_prune = num_candidates - min_old_cset_length; + + while (true) { + G1HeapRegion* r = old_regions->at(num_candidates - num_pruned - 1); + size_t const reclaimable = r->reclaimable_bytes(); + if (num_pruned >= max_to_prune || + wasted_bytes + reclaimable > allowed_waste) { + break; + } + r->rem_set()->clear(true /* cardset_only */); + + wasted_bytes += reclaimable; + num_pruned++; + } + + log_debug(gc, ergo, cset)("Pruned %u regions out of %u, leaving %zu bytes waste (allowed %zu)", + num_pruned, + num_candidates, + wasted_bytes, + allowed_waste); + + old_regions->trunc_to(num_candidates - num_pruned); +} + +static int compare_region_gc_efficiency(G1HeapRegion** rr1, G1HeapRegion** rr2) { + G1HeapRegion* r1 = *rr1; + G1HeapRegion* r2 = *rr2; + + assert(r1 != nullptr, "must be"); + assert(r2 != nullptr, "must be"); + + G1Policy* p = G1CollectedHeap::heap()->policy(); + double gc_efficiency1 = p->predict_gc_efficiency(r1); + double gc_efficiency2 = p->predict_gc_efficiency(r2); + + if (gc_efficiency1 > gc_efficiency2) { + return -1; + } else if (gc_efficiency1 < gc_efficiency2) { + return 1; + } else { + return 0; + } +} + +GrowableArrayCHeap* G1UpdateRegionLivenessAndSelectForRebuildTask::sort_and_prune_old_selected() { + // Nothing to do for the humongous candidates here. Old selected need to be pruned. + + if (_old_selected_for_rebuild.length() != 0) { + _old_selected_for_rebuild.sort(compare_region_gc_efficiency); + prune(&_old_selected_for_rebuild); + } + + return &_old_selected_for_rebuild; +} diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.hpp index a256693ff1d..6905419e2cc 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.hpp @@ -29,7 +29,7 @@ #include "gc/g1/g1HeapRegionManager.hpp" #include "gc/g1/g1HeapRegionSet.hpp" #include "gc/shared/workerThread.hpp" -#include "runtime/atomic.hpp" +#include "utilities/growableArray.hpp" class G1CollectedHeap; class G1ConcurrentMark; @@ -42,13 +42,15 @@ class G1UpdateRegionLivenessAndSelectForRebuildTask : public WorkerTask { G1ConcurrentMark* _cm; G1HeapRegionClaimer _hrclaimer; - Atomic _total_selected_for_rebuild; + GrowableArrayCHeap _old_selected_for_rebuild; + uint _num_humongous_selected_for_rebuild; // Reclaimed empty regions G1FreeRegionList _cleanup_list; struct G1OnRegionClosure; + void prune(GrowableArrayCHeap* old_regions); public: G1UpdateRegionLivenessAndSelectForRebuildTask(G1CollectedHeap* g1h, G1ConcurrentMark* cm, @@ -59,9 +61,14 @@ public: void work(uint worker_id) override; uint total_selected_for_rebuild() const { - return _total_selected_for_rebuild.load_relaxed(); + return (uint)_old_selected_for_rebuild.length() + _num_humongous_selected_for_rebuild; } + // Sort selected old regions by efficiency and prune them based on G1HeapWastePercent. + // This pruning improves rebuild time in addition to remembered set memory usage. + // Returns the set of regions selected in efficiency order. + GrowableArrayCHeap* sort_and_prune_old_selected(); + static uint desired_num_workers(uint num_regions); }; diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp index 629cbae935e..31d61b8b388 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -112,22 +112,22 @@ class G1ConcPhaseTimer : public GCTraceConcTimeImpl title("Concurrent %s Cycle", _state == FullMark ? "Mark" : "Undo"); + FormatBuffer<128> title("Concurrent %s Cycle", is_in_full_concurrent_cycle() ? "Mark" : "Undo"); GCTraceConcTime(Info, gc) tt(title); concurrent_cycle_start(); - if (_state == FullMark) { + if (_state == FullCycleMarking) { concurrent_mark_cycle_do(); } else { - assert(_state == UndoMark, "Must do undo mark but is %d", _state); + assert(_state == UndoCycleResetForNextCycle, "Must do undo mark but is %d", _state); concurrent_undo_cycle_do(); } - concurrent_cycle_end(_state == FullMark && !_cm->has_aborted()); + concurrent_cycle_end(is_in_full_concurrent_cycle() && !_cm->has_aborted()); update_perf_counter_cpu_time(); } @@ -135,7 +135,7 @@ void G1ConcurrentMarkThread::run_service() { } void G1ConcurrentMarkThread::stop_service() { - if (in_progress()) { + if (is_in_progress()) { // We are not allowed to abort the marking threads during root region scan. // Needs to be done separately. _cm->root_region_scan_abort_and_wait(); @@ -149,7 +149,7 @@ void G1ConcurrentMarkThread::stop_service() { bool G1ConcurrentMarkThread::wait_for_next_cycle() { MonitorLocker ml(G1CGC_lock, Mutex::_no_safepoint_check_flag); - while (!in_progress() && !should_terminate()) { + while (!is_in_progress() && !should_terminate()) { ml.wait(); } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp index 22be7d9ffbb..e75298fdcb4 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -32,25 +32,34 @@ class G1Policy; // The concurrent mark thread triggers the various steps of the concurrent marking // cycle, including various marking cleanup. +// +// The concurrent cycle may either be "Full" (i.e. include marking, rebuilding and +// scrubbing, resetting for the next cycle) or "Undo", i.e. shortened to just the +// reset part. class G1ConcurrentMarkThread: public ConcurrentGCThread { G1ConcurrentMark* _cm; enum ServiceState : uint { Idle, - FullMark, - UndoMark + FullCycleMarking, + FullCycleRebuildOrScrub, + FullCycleResetForNextCycle, + UndoCycleResetForNextCycle }; volatile ServiceState _state; + // Returns whether we are in a "Full" cycle. + bool is_in_full_concurrent_cycle() const; + // Wait for next cycle. Returns the command passed over. bool wait_for_next_cycle(); bool mark_loop_needs_restart() const; - // Phases and subphases for the full concurrent marking cycle in order. + // Phases and subphases for the full concurrent cycle in order. // - // All these methods return true if the marking should be aborted. + // All these methods return true if the cycle should be aborted. bool phase_clear_cld_claimed_marks(); bool phase_scan_root_regions(); @@ -88,22 +97,25 @@ class G1ConcurrentMarkThread: public ConcurrentGCThread { double total_mark_cpu_time_s(); // Cpu time used by all marking worker threads in seconds. double worker_threads_cpu_time_s(); - - G1ConcurrentMark* cm() { return _cm; } - + // State management. void set_idle(); - void start_full_mark(); - void start_undo_mark(); + void start_full_cycle(); + void start_undo_cycle(); - bool idle() const; + void set_full_cycle_rebuild_and_scrub(); + void set_full_cycle_reset_for_next_cycle(); + + bool is_idle() const; // Returns true from the moment a concurrent cycle is - // initiated (during the concurrent start pause when started() is set) - // to the moment when the cycle completes (just after the next - // marking bitmap has been cleared and in_progress() is - // cleared). - bool in_progress() const; + // initiated (during the concurrent start pause when calling one of the + // start_*_cycle() methods) to the moment when the cycle completes. + bool is_in_progress() const; - bool in_undo_mark() const; + bool is_in_marking() const; + bool is_in_rebuild_or_scrub() const; + bool is_in_reset_for_next_cycle() const; + + bool is_in_undo_cycle() const; // Update the perf data counter for concurrent mark. void update_perf_counter_cpu_time(); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp index 254eaf62bb2..8cb7881e000 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -32,7 +32,7 @@ // Total virtual time so far. inline double G1ConcurrentMarkThread::total_mark_cpu_time_s() { - return os::thread_cpu_time(this) + worker_threads_cpu_time_s(); + return static_cast(os::thread_cpu_time(this)) + worker_threads_cpu_time_s(); } // Marking virtual time so far @@ -40,29 +40,64 @@ inline double G1ConcurrentMarkThread::worker_threads_cpu_time_s() { return _cm->worker_threads_cpu_time_s(); } +inline bool G1ConcurrentMarkThread::is_in_full_concurrent_cycle() const { + ServiceState state = _state; + return (state == FullCycleMarking || state == FullCycleRebuildOrScrub || state == FullCycleResetForNextCycle); +} + inline void G1ConcurrentMarkThread::set_idle() { - assert(_state == FullMark || _state == UndoMark, "must not be starting a new cycle"); + // Concurrent cycle may be aborted any time. + assert(!is_idle(), "must not be idle"); _state = Idle; } -inline void G1ConcurrentMarkThread::start_full_mark() { - assert(_state == Idle, "cycle in progress"); - _state = FullMark; +inline void G1ConcurrentMarkThread::start_full_cycle() { + assert(SafepointSynchronize::is_at_safepoint(), "must be"); + assert(is_idle(), "cycle in progress"); + _state = FullCycleMarking; } -inline void G1ConcurrentMarkThread::start_undo_mark() { - assert(_state == Idle, "cycle in progress"); - _state = UndoMark; +inline void G1ConcurrentMarkThread::start_undo_cycle() { + assert(SafepointSynchronize::is_at_safepoint(), "must be"); + assert(is_idle(), "cycle in progress"); + _state = UndoCycleResetForNextCycle; } -inline bool G1ConcurrentMarkThread::idle() const { return _state == Idle; } - -inline bool G1ConcurrentMarkThread::in_progress() const { - return !idle(); +inline void G1ConcurrentMarkThread::set_full_cycle_rebuild_and_scrub() { + assert(SafepointSynchronize::is_at_safepoint(), "must be"); + assert(_state == FullCycleMarking, "must be"); + _state = FullCycleRebuildOrScrub; } -inline bool G1ConcurrentMarkThread::in_undo_mark() const { - return _state == UndoMark; +inline void G1ConcurrentMarkThread::set_full_cycle_reset_for_next_cycle() { + assert(SafepointSynchronize::is_at_safepoint(), "must be"); + assert(_state == FullCycleRebuildOrScrub, "must be"); + _state = FullCycleResetForNextCycle; +} + +inline bool G1ConcurrentMarkThread::is_in_marking() const { + return _state == FullCycleMarking; +} + +inline bool G1ConcurrentMarkThread::is_in_rebuild_or_scrub() const { + return _state == FullCycleRebuildOrScrub; +} + +inline bool G1ConcurrentMarkThread::is_in_reset_for_next_cycle() const { + ServiceState state = _state; + return state == FullCycleResetForNextCycle || state == UndoCycleResetForNextCycle; +} + +inline bool G1ConcurrentMarkThread::is_idle() const { + return _state == Idle; +} + +inline bool G1ConcurrentMarkThread::is_in_progress() const { + return !is_idle(); +} + +inline bool G1ConcurrentMarkThread::is_in_undo_cycle() const { + return _state == UndoCycleResetForNextCycle; } #endif // SHARE_GC_G1_G1CONCURRENTMARKTHREAD_INLINE_HPP diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index e8498250f85..c835dd159a6 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -353,7 +353,13 @@ void G1FullCollector::phase1_mark_live_objects() { scope()->tracer()->report_object_count_after_gc(&_is_alive, _heap->workers()); } #if TASKQUEUE_STATS - marking_task_queues()->print_and_reset_taskqueue_stats("Marking Task Queue"); + marking_task_queues()->print_and_reset_taskqueue_stats("Full GC"); + + auto get_stats = [&](uint i) { + return marker(i)->partial_array_splitter().stats(); + }; + PartialArrayTaskStats::log_set(_num_workers, get_stats, + "Full GC Partial Array"); #endif } diff --git a/src/hotspot/share/gc/g1/g1FullGCMarker.hpp b/src/hotspot/share/gc/g1/g1FullGCMarker.hpp index 5973cc841c5..82fe3655319 100644 --- a/src/hotspot/share/gc/g1/g1FullGCMarker.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCMarker.hpp @@ -89,6 +89,7 @@ public: ~G1FullGCMarker(); G1MarkTasksQueue* task_queue() { return &_task_queue; } + PartialArraySplitter& partial_array_splitter() { return _partial_array_splitter; } // Marking entry points template inline void mark_and_push(T* p); diff --git a/src/hotspot/share/gc/g1/g1GCPauseType.hpp b/src/hotspot/share/gc/g1/g1GCPauseType.hpp deleted file mode 100644 index 254edb28fea..00000000000 --- a/src/hotspot/share/gc/g1/g1GCPauseType.hpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2021, 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. - * - */ - -#ifndef SHARE_GC_G1_G1GCPAUSETYPES_HPP -#define SHARE_GC_G1_G1GCPAUSETYPES_HPP - -#include "utilities/debug.hpp" -#include "utilities/enumIterator.hpp" - -enum class G1GCPauseType : uint { - YoungGC, - LastYoungGC, - ConcurrentStartMarkGC, - ConcurrentStartUndoGC, - Cleanup, - Remark, - MixedGC, - FullGC -}; - -ENUMERATOR_RANGE(G1GCPauseType, G1GCPauseType::YoungGC, G1GCPauseType::FullGC) - -class G1GCPauseTypeHelper { -public: - - static void assert_is_young_pause(G1GCPauseType type) { - assert(type != G1GCPauseType::FullGC, "must be"); - assert(type != G1GCPauseType::Remark, "must be"); - assert(type != G1GCPauseType::Cleanup, "must be"); - } - - static bool is_young_only_pause(G1GCPauseType type) { - assert_is_young_pause(type); - return type == G1GCPauseType::ConcurrentStartUndoGC || - type == G1GCPauseType::ConcurrentStartMarkGC || - type == G1GCPauseType::LastYoungGC || - type == G1GCPauseType::YoungGC; - } - - static bool is_mixed_pause(G1GCPauseType type) { - assert_is_young_pause(type); - return type == G1GCPauseType::MixedGC; - } - - static bool is_last_young_pause(G1GCPauseType type) { - assert_is_young_pause(type); - return type == G1GCPauseType::LastYoungGC; - } - - static bool is_concurrent_start_pause(G1GCPauseType type) { - assert_is_young_pause(type); - return type == G1GCPauseType::ConcurrentStartMarkGC || type == G1GCPauseType::ConcurrentStartUndoGC; - } - - static const char* to_string(G1GCPauseType type) { - static const char* pause_strings[] = { "Normal", - "Prepare Mixed", - "Concurrent Start", // Do not distinguish between the different - "Concurrent Start", // Concurrent Start pauses. - "Cleanup", - "Remark", - "Mixed", - "Full" }; - return pause_strings[static_cast(type)]; - } -}; - -#endif // SHARE_GC_G1_G1GCPAUSETYPES_HPP diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.hpp b/src/hotspot/share/gc/g1/g1HeapRegion.hpp index 2b4b640d52b..ec9cab26049 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.hpp @@ -567,41 +567,15 @@ public: // G1HeapRegionClosure is used for iterating over regions. // Terminates the iteration when the "do_heap_region" method returns "true". class G1HeapRegionClosure : public StackObj { - friend class G1HeapRegionManager; - friend class G1CollectionSet; - friend class G1CollectionSetCandidates; - - bool _is_complete; - void set_incomplete() { _is_complete = false; } - public: - G1HeapRegionClosure(): _is_complete(true) {} - // Typically called on each region until it returns true. virtual bool do_heap_region(G1HeapRegion* r) = 0; - - // True after iteration if the closure was applied to all heap regions - // and returned "false" in all cases. - bool is_complete() { return _is_complete; } }; class G1HeapRegionIndexClosure : public StackObj { - friend class G1HeapRegionManager; - friend class G1CollectionSet; - friend class G1CollectionSetCandidates; - - bool _is_complete; - void set_incomplete() { _is_complete = false; } - public: - G1HeapRegionIndexClosure(): _is_complete(true) {} - // Typically called on each region until it returns true. virtual bool do_heap_region_index(uint region_index) = 0; - - // True after iteration if the closure was applied to all heap regions - // and returned "false" in all cases. - bool is_complete() { return _is_complete; } }; #endif // SHARE_GC_G1_G1HEAPREGION_HPP diff --git a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp index fdd3b919590..3c0318827ef 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp @@ -511,7 +511,6 @@ void G1HeapRegionManager::iterate(G1HeapRegionClosure* blk) const { guarantee(at(i) != nullptr, "Tried to access region %u that has a null G1HeapRegion*", i); bool res = blk->do_heap_region(at(i)); if (res) { - blk->set_incomplete(); return; } } @@ -526,7 +525,6 @@ void G1HeapRegionManager::iterate(G1HeapRegionIndexClosure* blk) const { } bool res = blk->do_heap_region_index(i); if (res) { - blk->set_incomplete(); return; } } diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp index a2a9bc8e857..dd7a8aa117d 100644 --- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp +++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -236,7 +236,7 @@ private: VerifyOption _vo; bool _failures; - bool is_in_full_gc() const { return G1CollectedHeap::heap()->collector_state()->in_full_gc(); } + bool is_in_full_gc() const { return G1CollectedHeap::heap()->collector_state()->is_in_full_gc(); } public: VerifyRegionClosure(VerifyOption vo) @@ -349,7 +349,7 @@ void G1HeapVerifier::verify(VerifyOption vo) { bool failures = rootsCl.failures() || codeRootsCl.failures(); - if (!_g1h->policy()->collector_state()->in_full_gc()) { + if (!_g1h->policy()->collector_state()->is_in_full_gc()) { // If we're verifying during a full GC then the region sets // will have been torn down at the start of the GC. Therefore // verifying the region sets will fail. So we only verify @@ -494,7 +494,7 @@ public: }; void G1HeapVerifier::verify_marking_state() { - assert(G1CollectedHeap::heap()->collector_state()->in_concurrent_start_gc(), "must be"); + assert(G1CollectedHeap::heap()->collector_state()->is_in_concurrent_start_gc(), "must be"); // Verify TAMSes, bitmaps and liveness statistics. // diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp index 75a8ef1a336..cb857dc6eab 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp @@ -741,7 +741,7 @@ void G1ParScanThreadStateSet::print_partial_array_task_stats() { return state_for_worker(i)->partial_array_task_stats(); }; PartialArrayTaskStats::log_set(_num_workers, get_stats, - "Partial Array Task Stats"); + "Young GC Partial Array"); } #endif // TASKQUEUE_STATS diff --git a/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp b/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp index f280d76f3c7..ee11bbd961f 100644 --- a/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp +++ b/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -38,8 +38,8 @@ bool G1PeriodicGCTask::should_start_periodic_gc(G1CollectedHeap* g1h, // Ensure no GC safepoints while we're doing the checks, to avoid data races. SuspendibleThreadSetJoiner sts; - // If we are currently in a concurrent mark we are going to uncommit memory soon. - if (g1h->concurrent_mark()->in_progress()) { + // If we are currently in a concurrent cycle we are going to uncommit memory soon. + if (g1h->collector_state()->is_in_concurrent_cycle()) { log_debug(gc, periodic)("Concurrent cycle in progress. Skipping."); return false; } diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 98e6acc1d77..dce83a3f084 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -28,7 +28,6 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSet.hpp" #include "gc/g1/g1CollectionSetCandidates.inline.hpp" -#include "gc/g1/g1CollectionSetChooser.hpp" #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/g1ConcurrentRefine.hpp" @@ -177,7 +176,7 @@ uint G1Policy::calculate_desired_eden_length_by_mmu() const { void G1Policy::update_young_length_bounds() { assert(!Universe::is_fully_initialized() || SafepointSynchronize::is_at_safepoint(), "must be"); - bool for_young_only_phase = collector_state()->in_young_only_phase(); + bool for_young_only_phase = collector_state()->is_in_young_only_phase(); update_young_length_bounds(_analytics->predict_pending_cards(for_young_only_phase), _analytics->predict_card_rs_length(for_young_only_phase), _analytics->predict_code_root_rs_length(for_young_only_phase)); @@ -505,7 +504,7 @@ uint G1Policy::calculate_desired_eden_length_before_mixed(double base_time_ms, double G1Policy::predict_survivor_regions_evac_time() const { double survivor_regions_evac_time = predict_young_region_other_time_ms(_g1h->survivor()->length()); for (G1HeapRegion* r : _g1h->survivor()->regions()) { - survivor_regions_evac_time += predict_region_copy_time_ms(r, _g1h->collector_state()->in_young_only_phase()); + survivor_regions_evac_time += predict_region_copy_time_ms(r, _g1h->collector_state()->is_in_young_only_phase()); } return survivor_regions_evac_time; @@ -561,8 +560,7 @@ void G1Policy::revise_young_list_target_length(size_t pending_cards, size_t card void G1Policy::record_full_collection_start() { record_pause_start_time(); // Release the future to-space so that it is available for compaction into. - collector_state()->set_in_young_only_phase(false); - collector_state()->set_in_full_gc(true); + collector_state()->set_in_full_gc(); _collection_set->abandon_all_candidates(); } @@ -571,17 +569,10 @@ void G1Policy::record_full_collection_end(size_t allocation_word_size) { // since last pause. double end_sec = os::elapsedTime(); - collector_state()->set_in_full_gc(false); - // "Nuke" the heuristics that control the young/mixed GC // transitions and make sure we start with young GCs after the Full GC. - collector_state()->set_in_young_only_phase(true); - collector_state()->set_in_young_gc_before_mixed(false); + collector_state()->set_in_normal_young_gc(); collector_state()->set_initiate_conc_mark_if_possible(need_to_start_conc_mark("end of Full GC", allocation_word_size)); - collector_state()->set_in_concurrent_start_gc(false); - collector_state()->set_mark_in_progress(false); - collector_state()->set_mark_or_rebuild_in_progress(false); - collector_state()->set_clear_bitmap_in_progress(false); _eden_surv_rate_group->start_adding_regions(); // also call this on any additional surv rate groups @@ -593,7 +584,7 @@ void G1Policy::record_full_collection_end(size_t allocation_word_size) { _old_gen_alloc_tracker.reset_after_gc(_g1h->humongous_regions_count() * G1HeapRegion::GrainBytes); double start_time_sec = cur_pause_start_sec(); - record_pause(G1GCPauseType::FullGC, start_time_sec, end_sec); + record_pause(Pause::Full, start_time_sec, end_sec); } static void log_refinement_stats(const G1ConcurrentRefineStats& stats) { @@ -700,7 +691,7 @@ void G1Policy::record_young_collection_start() { void G1Policy::record_concurrent_mark_init_end() { assert(!collector_state()->initiate_conc_mark_if_possible(), "we should have cleared it by now"); - collector_state()->set_in_concurrent_start_gc(false); + collector_state()->set_in_normal_young_gc(); } void G1Policy::record_concurrent_mark_remark_end() { @@ -708,8 +699,7 @@ void G1Policy::record_concurrent_mark_remark_end() { double start_time_sec = cur_pause_start_sec(); double elapsed_time_ms = (end_time_sec - start_time_sec) * 1000.0; _analytics->report_concurrent_mark_remark_times_ms(elapsed_time_ms); - record_pause(G1GCPauseType::Remark, start_time_sec, end_time_sec); - collector_state()->set_mark_in_progress(false); + record_pause(Pause::Remark, start_time_sec, end_time_sec); } G1CollectionSetCandidates* G1Policy::candidates() const { @@ -739,7 +729,7 @@ double G1Policy::constant_other_time_ms(double pause_time_ms) const { } bool G1Policy::about_to_start_mixed_phase() const { - return _g1h->concurrent_mark()->in_progress() || collector_state()->in_young_gc_before_mixed(); + return collector_state()->is_in_concurrent_cycle() || collector_state()->is_in_young_gc_before_mixed(); } bool G1Policy::need_to_start_conc_mark(const char* source, size_t allocation_word_size) { @@ -752,7 +742,7 @@ bool G1Policy::need_to_start_conc_mark(const char* source, size_t allocation_wor bool result = false; if (non_young_occupancy > marking_initiating_used_threshold) { - result = collector_state()->in_young_only_phase(); + result = collector_state()->is_in_young_only_phase(); log_debug(gc, ergo, ihop)("%s non-young occupancy: %zuB allocation request: %zuB threshold: %zuB (%1.2f) source: %s", result ? "Request concurrent cycle initiation (occupancy higher than threshold)" : "Do not request concurrent cycle initiation (still doing mixed collections)", non_young_occupancy, allocation_word_size * HeapWordSize, marking_initiating_used_threshold, (double) marking_initiating_used_threshold / _g1h->capacity() * 100, source); @@ -761,7 +751,7 @@ bool G1Policy::need_to_start_conc_mark(const char* source, size_t allocation_wor } bool G1Policy::concurrent_operation_is_full_mark(const char* msg, size_t allocation_word_size) { - return collector_state()->in_concurrent_start_gc() && + return collector_state()->is_in_concurrent_start_gc() && ((_g1h->gc_cause() != GCCause::_g1_humongous_allocation) || need_to_start_conc_mark(msg, allocation_word_size)); } @@ -800,10 +790,10 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar double end_time_sec = Ticks::now().seconds(); double pause_time_ms = (end_time_sec - start_time_sec) * 1000.0; - G1GCPauseType this_pause = collector_state()->young_gc_pause_type(concurrent_operation_is_full_mark); - bool is_young_only_pause = G1GCPauseTypeHelper::is_young_only_pause(this_pause); + Pause this_pause = collector_state()->gc_pause_type(concurrent_operation_is_full_mark); + bool is_young_only_pause = G1CollectorState::is_young_only_pause(this_pause); - if (G1GCPauseTypeHelper::is_concurrent_start_pause(this_pause)) { + if (G1CollectorState::is_concurrent_start_pause(this_pause)) { record_concurrent_mark_init_end(); } else { maybe_start_marking(allocation_word_size); @@ -945,20 +935,19 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar record_pause(this_pause, start_time_sec, end_time_sec); - if (G1GCPauseTypeHelper::is_last_young_pause(this_pause)) { - assert(!G1GCPauseTypeHelper::is_concurrent_start_pause(this_pause), + if (G1CollectorState::is_last_young_pause(this_pause)) { + assert(!G1CollectorState::is_concurrent_start_pause(this_pause), "The young GC before mixed is not allowed to be concurrent start GC"); // This has been the young GC before we start doing mixed GCs. We already // decided to start mixed GCs much earlier, so there is nothing to do except // advancing the state. - collector_state()->set_in_young_only_phase(false); - collector_state()->set_in_young_gc_before_mixed(false); - } else if (G1GCPauseTypeHelper::is_mixed_pause(this_pause)) { + collector_state()->set_in_space_reclamation_phase(); + } else if (G1CollectorState::is_mixed_pause(this_pause)) { // This is a mixed GC. Here we decide whether to continue doing more // mixed GCs or not. if (!next_gc_should_be_mixed()) { log_debug(gc, ergo)("do not continue mixed GCs (candidate old regions not available)"); - collector_state()->set_in_young_only_phase(true); + collector_state()->set_in_normal_young_gc(); assert(!candidates()->has_more_marking_candidates(), "only end mixed if all candidates from marking were processed"); @@ -971,12 +960,8 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar _eden_surv_rate_group->start_adding_regions(); - assert(!(G1GCPauseTypeHelper::is_concurrent_start_pause(this_pause) && collector_state()->mark_or_rebuild_in_progress()), - "If the last pause has been concurrent start, we should not have been in the marking window"); - if (G1GCPauseTypeHelper::is_concurrent_start_pause(this_pause)) { - collector_state()->set_mark_in_progress(concurrent_operation_is_full_mark); - collector_state()->set_mark_or_rebuild_in_progress(concurrent_operation_is_full_mark); - } + assert(!(G1CollectorState::is_concurrent_start_pause(this_pause) && collector_state()->is_in_concurrent_cycle()), + "If the last pause has been concurrent start, we should not have been in the marking cycle"); _free_regions_at_end_of_collection = _g1h->num_free_regions(); @@ -987,7 +972,7 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar _old_gen_alloc_tracker.reset_after_gc(_g1h->humongous_regions_count() * G1HeapRegion::GrainBytes); if (update_ihop_prediction(app_time_ms / 1000.0, - G1GCPauseTypeHelper::is_young_only_pause(this_pause))) { + G1CollectorState::is_young_only_pause(this_pause))) { _ihop_control->report_statistics(_g1h->gc_tracer_stw(), _g1h->non_young_occupancy_after_allocation(allocation_word_size)); } } else { @@ -1081,7 +1066,7 @@ void G1Policy::record_young_gc_pause_end(bool evacuation_failed) { double G1Policy::predict_base_time_ms(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length) const { - bool in_young_only_phase = collector_state()->in_young_only_phase(); + bool in_young_only_phase = collector_state()->is_in_young_only_phase(); // Cards from the refinement table and the cards from the young gen remset are // unique to each other as they are located on the card table. @@ -1105,7 +1090,7 @@ double G1Policy::predict_base_time_ms(size_t pending_cards, } double G1Policy::predict_base_time_ms(size_t pending_cards, size_t card_rs_length) const { - bool for_young_only_phase = collector_state()->in_young_only_phase(); + bool for_young_only_phase = collector_state()->is_in_young_only_phase(); size_t code_root_rs_length = _analytics->predict_code_root_rs_length(for_young_only_phase); return predict_base_time_ms(pending_cards, card_rs_length, code_root_rs_length); } @@ -1145,7 +1130,7 @@ double G1Policy::predict_eden_copy_time_ms(uint count, size_t* bytes_to_copy) co if (bytes_to_copy != nullptr) { *bytes_to_copy = expected_bytes; } - return _analytics->predict_object_copy_time_ms(expected_bytes, collector_state()->in_young_only_phase()); + return _analytics->predict_object_copy_time_ms(expected_bytes, collector_state()->is_in_young_only_phase()); } double G1Policy::predict_region_copy_time_ms(G1HeapRegion* hr, bool for_young_only_phase) const { @@ -1235,7 +1220,7 @@ bool G1Policy::force_concurrent_start_if_outside_cycle(GCCause::Cause gc_cause) // We actually check whether we are marking here and not if we are in a // reclamation phase. This means that we will schedule a concurrent mark // even while we are still in the process of reclaiming memory. - bool during_cycle = _g1h->concurrent_mark()->in_progress(); + bool during_cycle = collector_state()->is_in_concurrent_cycle(); if (!during_cycle) { log_debug(gc, ergo)("Request concurrent cycle initiation (requested by GC cause). " "GC cause: %s", @@ -1251,8 +1236,7 @@ bool G1Policy::force_concurrent_start_if_outside_cycle(GCCause::Cause gc_cause) } void G1Policy::initiate_conc_mark() { - collector_state()->set_in_concurrent_start_gc(true); - collector_state()->set_initiate_conc_mark_if_possible(false); + collector_state()->set_in_concurrent_start_gc(); } static const char* requester_for_mixed_abort(GCCause::Cause cause) { @@ -1274,7 +1258,7 @@ void G1Policy::decide_on_concurrent_start_pause() { // will set it here if we have to. However, it should be cleared by // the end of the pause (it's only set for the duration of a // concurrent start pause). - assert(!collector_state()->in_concurrent_start_gc(), "pre-condition"); + assert(!collector_state()->is_in_concurrent_start_gc(), "pre-condition"); if (collector_state()->initiate_conc_mark_if_possible()) { // We had noticed on a previous pause that the heap occupancy has @@ -1287,7 +1271,7 @@ void G1Policy::decide_on_concurrent_start_pause() { if ((cause != GCCause::_wb_breakpoint) && ConcurrentGCBreakpoints::is_controlled()) { log_debug(gc, ergo)("Do not initiate concurrent cycle (whitebox controlled)"); - } else if (!about_to_start_mixed_phase() && collector_state()->in_young_only_phase()) { + } else if (!about_to_start_mixed_phase() && collector_state()->is_in_young_only_phase()) { // Initiate a new concurrent start if there is no marking or reclamation going on. initiate_conc_mark(); log_debug(gc, ergo)("Initiate concurrent cycle (concurrent cycle initiation requested)"); @@ -1296,8 +1280,7 @@ void G1Policy::decide_on_concurrent_start_pause() { (cause == GCCause::_wb_breakpoint)) { // Initiate a concurrent start. A concurrent start must be a young only // GC, so the collector state must be updated to reflect this. - collector_state()->set_in_young_only_phase(true); - collector_state()->set_in_young_gc_before_mixed(false); + collector_state()->set_in_normal_young_gc(); // We might have ended up coming here about to start a mixed phase with a collection set // active. The following remark might change the change the "evacuation efficiency" of @@ -1326,10 +1309,10 @@ void G1Policy::decide_on_concurrent_start_pause() { } // Result consistency checks. // We do not allow concurrent start to be piggy-backed on a mixed GC. - assert(!collector_state()->in_concurrent_start_gc() || - collector_state()->in_young_only_phase(), "sanity"); - // We also do not allow mixed GCs during marking. - assert(!collector_state()->mark_or_rebuild_in_progress() || collector_state()->in_young_only_phase(), "sanity"); + assert(!collector_state()->is_in_concurrent_start_gc() || + collector_state()->is_in_young_only_phase(), "sanity"); + // We also do not allow mixed GCs during marking/rebuilding. + assert(!collector_state()->is_in_mark_or_rebuild() || collector_state()->is_in_young_only_phase(), "sanity %d %d", collector_state()->is_in_concurrent_cycle(), collector_state()->is_in_young_only_phase()); } void G1Policy::record_concurrent_mark_cleanup_end(bool has_rebuilt_remembered_sets) { @@ -1348,16 +1331,16 @@ void G1Policy::record_concurrent_mark_cleanup_end(bool has_rebuilt_remembered_se abort_time_to_mixed_tracking(); log_debug(gc, ergo)("request young-only gcs (candidate old regions not available)"); } - collector_state()->set_in_young_gc_before_mixed(mixed_gc_pending); - collector_state()->set_mark_or_rebuild_in_progress(false); - collector_state()->set_clear_bitmap_in_progress(true); + if (mixed_gc_pending) { + collector_state()->set_in_young_gc_before_mixed(); + } double end_sec = os::elapsedTime(); double start_sec = cur_pause_start_sec(); double elapsed_time_ms = (end_sec - start_sec) * 1000.0; _analytics->report_concurrent_mark_cleanup_times_ms(elapsed_time_ms); - record_pause(G1GCPauseType::Cleanup, start_sec, end_sec); + record_pause(Pause::Cleanup, start_sec, end_sec); } void G1Policy::abandon_collection_set_candidates() { @@ -1373,25 +1356,25 @@ void G1Policy::maybe_start_marking(size_t allocation_word_size) { } } -void G1Policy::update_gc_pause_time_ratios(G1GCPauseType gc_type, double start_time_sec, double end_time_sec) { +void G1Policy::update_gc_pause_time_ratios(Pause gc_type, double start_time_sec, double end_time_sec) { double pause_time_sec = end_time_sec - start_time_sec; double pause_time_ms = pause_time_sec * 1000.0; _analytics->update_gc_time_ratios(end_time_sec, pause_time_ms); - if (gc_type == G1GCPauseType::Cleanup || gc_type == G1GCPauseType::Remark) { + if (G1CollectorState::is_concurrent_cycle_pause(gc_type)) { _analytics->append_prev_collection_pause_end_ms(pause_time_ms); } else { _analytics->set_prev_collection_pause_end_ms(end_time_sec * 1000.0); } } -void G1Policy::record_pause(G1GCPauseType gc_type, +void G1Policy::record_pause(Pause gc_type, double start, double end) { // Manage the MMU tracker. For some reason it ignores Full GCs. - if (gc_type != G1GCPauseType::FullGC) { + if (gc_type != Pause::Full) { _mmu_tracker->add_pause(start, end); } @@ -1403,21 +1386,21 @@ void G1Policy::record_pause(G1GCPauseType gc_type, _analytics->set_gc_cpu_time_at_pause_end_ms(elapsed_gc_cpu_time); } -void G1Policy::update_time_to_mixed_tracking(G1GCPauseType gc_type, +void G1Policy::update_time_to_mixed_tracking(Pause gc_type, double start, double end) { // Manage the mutator time tracking from concurrent start to first mixed gc. switch (gc_type) { - case G1GCPauseType::FullGC: + case Pause::Full: abort_time_to_mixed_tracking(); break; - case G1GCPauseType::Cleanup: - case G1GCPauseType::Remark: - case G1GCPauseType::YoungGC: - case G1GCPauseType::LastYoungGC: + case Pause::Cleanup: + case Pause::Remark: + case Pause::Normal: + case Pause::LastYoung: _concurrent_start_to_mixed.add_pause(end - start); break; - case G1GCPauseType::ConcurrentStartMarkGC: + case Pause::ConcurrentStartFull: // Do not track time-to-mixed time for periodic collections as they are likely // to be not representative to regular operation as the mutators are idle at // that time. Also only track full concurrent mark cycles. @@ -1425,12 +1408,12 @@ void G1Policy::update_time_to_mixed_tracking(G1GCPauseType gc_type, _concurrent_start_to_mixed.record_concurrent_start_end(end); } break; - case G1GCPauseType::ConcurrentStartUndoGC: + case Pause::ConcurrentStartUndo: assert(_g1h->gc_cause() == GCCause::_g1_humongous_allocation, "GC cause must be humongous allocation but is %d", _g1h->gc_cause()); break; - case G1GCPauseType::MixedGC: + case Pause::Mixed: _concurrent_start_to_mixed.record_mixed_gc_start(start); break; default: diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index 9513c79869e..585e26441d5 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -56,7 +56,7 @@ class GCPolicyCounters; class STWGCTimer; class G1Policy: public CHeapObj { - private: + using Pause = G1CollectorState::Pause; static G1IHOPControl* create_ihop_control(const G1OldGenAllocationTracker* old_gen_alloc_tracker, const G1Predictions* predictor); @@ -115,7 +115,7 @@ class G1Policy: public CHeapObj { G1ConcurrentStartToMixedTimeTracker _concurrent_start_to_mixed; bool should_update_surv_rate_group_predictors() { - return collector_state()->in_young_only_phase() && !collector_state()->mark_or_rebuild_in_progress(); + return collector_state()->is_in_young_only_phase() && !collector_state()->is_in_mark_or_rebuild(); } double pending_cards_processing_time() const; @@ -268,13 +268,13 @@ private: // Sets up marking if proper conditions are met. void maybe_start_marking(size_t allocation_word_size); // Manage time-to-mixed tracking. - void update_time_to_mixed_tracking(G1GCPauseType gc_type, double start, double end); + void update_time_to_mixed_tracking(Pause gc_type, double start, double end); // Record the given STW pause with the given start and end times (in s). - void record_pause(G1GCPauseType gc_type, + void record_pause(Pause gc_type, double start, double end); - void update_gc_pause_time_ratios(G1GCPauseType gc_type, double start_sec, double end_sec); + void update_gc_pause_time_ratios(Pause gc_type, double start_sec, double end_sec); // Indicate that we aborted marking before doing any mixed GCs. void abort_time_to_mixed_tracking(); diff --git a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp index 4dcdd33846e..df76147f4b1 100644 --- a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp +++ b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp @@ -95,7 +95,7 @@ private: // Evict a given element of the statistics cache. void evict(uint idx); - size_t _num_cache_entries_mask; + const uint _num_cache_entries_mask; uint hash(uint idx) { return idx & _num_cache_entries_mask; diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index 0c9a0fad8f2..4b4a8a68c30 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -1026,7 +1026,7 @@ class G1MergeHeapRootsTask : public WorkerTask { // the pause occurs during the Concurrent Cleanup for Next Mark phase. // Only at that point the region's bitmap may contain marks while being in the collection // set at the same time. - return _g1h->collector_state()->clear_bitmap_in_progress() && + return _g1h->collector_state()->is_in_reset_for_next_cycle() && hr->is_old(); } diff --git a/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp b/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp index 0c9973c520d..94f5466b8e0 100644 --- a/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp +++ b/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -23,12 +23,16 @@ */ #include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1CollectionSetChooser.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "gc/g1/g1HeapRegionRemSet.inline.hpp" #include "gc/g1/g1RemSetTrackingPolicy.hpp" #include "runtime/safepoint.hpp" +static bool region_occupancy_low_enough_for_evac(size_t live_bytes) { + size_t mixed_gc_live_threshold_bytes = G1HeapRegion::GrainBytes * (size_t)G1MixedGCLiveThresholdPercent / 100; + return live_bytes < mixed_gc_live_threshold_bytes; +} + void G1RemSetTrackingPolicy::update_at_allocate(G1HeapRegion* r) { assert(r->is_young() || r->is_humongous() || r->is_old(), "Region %u with unexpected heap region type %s", r->hrm_index(), r->get_type_str()); @@ -75,7 +79,8 @@ bool G1RemSetTrackingPolicy::update_old_before_rebuild(G1HeapRegion* r) { bool selected_for_rebuild = false; - if (G1CollectionSetChooser::region_occupancy_low_enough_for_evac(r->live_bytes()) && + if (region_occupancy_low_enough_for_evac(r->live_bytes()) && + !G1CollectedHeap::heap()->is_old_gc_alloc_region(r) && !r->rem_set()->is_tracked()) { r->rem_set()->set_state_updating(); selected_for_rebuild = true; diff --git a/src/hotspot/share/gc/g1/g1RootClosures.cpp b/src/hotspot/share/gc/g1/g1RootClosures.cpp index f03681487cb..2d5150c27aa 100644 --- a/src/hotspot/share/gc/g1/g1RootClosures.cpp +++ b/src/hotspot/share/gc/g1/g1RootClosures.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -72,7 +72,7 @@ G1EvacuationRootClosures* G1EvacuationRootClosures::create_root_closures(G1Colle G1ParScanThreadState* pss, bool process_only_dirty_klasses) { G1EvacuationRootClosures* res = nullptr; - if (g1h->collector_state()->in_concurrent_start_gc()) { + if (g1h->collector_state()->is_in_concurrent_start_gc()) { if (ClassUnloadingWithConcurrentMark) { res = new G1ConcurrentStartMarkClosures(g1h, pss); } else { diff --git a/src/hotspot/share/gc/g1/g1Trace.cpp b/src/hotspot/share/gc/g1/g1Trace.cpp index ed6a91f41ed..242a97ca4e3 100644 --- a/src/hotspot/share/gc/g1/g1Trace.cpp +++ b/src/hotspot/share/gc/g1/g1Trace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -23,7 +23,6 @@ */ #include "gc/g1/g1EvacInfo.hpp" -#include "gc/g1/g1GCPauseType.hpp" #include "gc/g1/g1HeapRegionTraceType.hpp" #include "gc/g1/g1Trace.hpp" #include "gc/shared/gcHeapSummary.hpp" @@ -48,12 +47,12 @@ public: class G1YCTypeConstant : public JfrSerializer { public: void serialize(JfrCheckpointWriter& writer) { - constexpr EnumRange types{}; + constexpr EnumRange types{}; static const u4 nof_entries = static_cast(types.size()); writer.write_count(nof_entries); for (auto index : types) { writer.write_key(static_cast(index)); - writer.write(G1GCPauseTypeHelper::to_string(index)); + writer.write(G1CollectorState::to_string(index)); } } }; @@ -72,8 +71,8 @@ void G1NewTracer::initialize() { JFR_ONLY(register_jfr_type_constants();) } -void G1NewTracer::report_young_gc_pause(G1GCPauseType pause) { - G1GCPauseTypeHelper::assert_is_young_pause(pause); +void G1NewTracer::report_young_gc_pause(G1CollectorState::Pause pause) { + G1CollectorState::assert_is_young_pause(pause); _pause = pause; } @@ -128,7 +127,7 @@ void G1NewTracer::report_adaptive_ihop_statistics(size_t threshold, void G1NewTracer::send_g1_young_gc_event() { // Check that the pause type has been updated to something valid for this event. - G1GCPauseTypeHelper::assert_is_young_pause(_pause); + G1CollectorState::assert_is_young_pause(_pause); EventG1GarbageCollection e(UNTIMED); if (e.should_commit()) { diff --git a/src/hotspot/share/gc/g1/g1Trace.hpp b/src/hotspot/share/gc/g1/g1Trace.hpp index a2e11ed4496..bfcc275d2ca 100644 --- a/src/hotspot/share/gc/g1/g1Trace.hpp +++ b/src/hotspot/share/gc/g1/g1Trace.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -25,7 +25,7 @@ #ifndef SHARE_GC_G1_G1TRACE_HPP #define SHARE_GC_G1_G1TRACE_HPP -#include "gc/g1/g1GCPauseType.hpp" +#include "gc/g1/g1CollectorState.hpp" #include "gc/shared/gcTrace.hpp" class G1EvacInfo; @@ -33,17 +33,17 @@ class G1HeapSummary; class G1EvacSummary; class G1NewTracer : public YoungGCTracer, public CHeapObj { - G1GCPauseType _pause; + G1CollectorState::Pause _pause; public: G1NewTracer() : YoungGCTracer(G1New), - _pause(G1GCPauseType::FullGC) // Initialize to something invalid. For this event, which + _pause(G1CollectorState::Pause::Full) // Initialize to something invalid. For this event, which // is about young collections, FullGC is not a valid value. { } void initialize(); - void report_young_gc_pause(G1GCPauseType pause); + void report_young_gc_pause(G1CollectorState::Pause pause); void report_gc_end_impl(const Ticks& timestamp, TimePartitions* time_partitions); void report_evacuation_info(G1EvacInfo* info); void report_evacuation_failed(EvacuationFailedInfo& ef_info); diff --git a/src/hotspot/share/gc/g1/g1VMOperations.cpp b/src/hotspot/share/gc/g1/g1VMOperations.cpp index 56ab3a4b0fe..f98f0b078f3 100644 --- a/src/hotspot/share/gc/g1/g1VMOperations.cpp +++ b/src/hotspot/share/gc/g1/g1VMOperations.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -84,8 +84,9 @@ void VM_G1TryInitiateConcMark::doit() { GCCauseSetter x(g1h, _gc_cause); - _mark_in_progress = g1h->collector_state()->mark_in_progress(); - _cycle_already_in_progress = g1h->concurrent_mark()->in_progress(); + G1CollectorState* state = g1h->collector_state(); + _mark_in_progress = state->is_in_marking(); + _cycle_already_in_progress = state->is_in_concurrent_cycle(); if (!g1h->policy()->force_concurrent_start_if_outside_cycle(_gc_cause)) { // Failure to force the next GC pause to be a concurrent start indicates @@ -166,11 +167,11 @@ void VM_G1PauseConcurrent::doit_epilogue() { } void VM_G1PauseRemark::work() { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - g1h->concurrent_mark()->remark(); + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + cm->remark(); } void VM_G1PauseCleanup::work() { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - g1h->concurrent_mark()->cleanup(); + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + cm->cleanup(); } diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index a9db9a7c269..04ccac5ff05 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -70,7 +70,7 @@ class G1YoungGCTraceTime { G1YoungCollector* _collector; - G1GCPauseType _pause_type; + G1CollectorState::Pause _pause_type; GCCause::Cause _pause_cause; static const uint MaxYoungGCNameLength = 128; @@ -93,7 +93,7 @@ class G1YoungGCTraceTime { os::snprintf_checked(_young_gc_name_data, MaxYoungGCNameLength, "Pause Young (%s) (%s)%s", - G1GCPauseTypeHelper::to_string(_pause_type), + G1CollectorState::to_string(_pause_type), GCCause::to_string(_pause_cause), evacuation_failed_string); return _young_gc_name_data; @@ -105,7 +105,7 @@ public: // Take snapshot of current pause type at start as it may be modified during gc. // The strings for all Concurrent Start pauses are the same, so the parameter // does not matter here. - _pause_type(_collector->collector_state()->young_gc_pause_type(false /* concurrent_operation_is_full_mark */)), + _pause_type(_collector->collector_state()->gc_pause_type(false /* concurrent_operation_is_full_mark */)), _pause_cause(cause), // Fake a "no cause" and manually add the correct string in update_young_gc_name() // to make the string look more natural. @@ -142,7 +142,7 @@ public: G1YoungGCJFRTracerMark(STWGCTimer* gc_timer_stw, G1NewTracer* gc_tracer_stw, GCCause::Cause cause) : G1JFRTracerMark(gc_timer_stw, gc_tracer_stw), _evacuation_info() { } - void report_pause_type(G1GCPauseType type) { + void report_pause_type(G1CollectorState::Pause type) { tracer()->report_young_gc_pause(type); } @@ -160,9 +160,9 @@ class G1YoungGCVerifierMark : public StackObj { static G1HeapVerifier::G1VerifyType young_collection_verify_type() { G1CollectorState* state = G1CollectedHeap::heap()->collector_state(); - if (state->in_concurrent_start_gc()) { + if (state->is_in_concurrent_start_gc()) { return G1HeapVerifier::G1VerifyConcurrentStart; - } else if (state->in_young_only_phase()) { + } else if (state->is_in_young_only_phase()) { return G1HeapVerifier::G1VerifyYoungNormal; } else { return G1HeapVerifier::G1VerifyMixed; @@ -391,7 +391,7 @@ class G1PrepareEvacuationTask : public WorkerTask { if (!obj->is_typeArray()) { // All regions that were allocated before marking have a TAMS != bottom. bool allocated_before_mark_start = region->bottom() != _g1h->concurrent_mark()->top_at_mark_start(region); - bool mark_in_progress = _g1h->collector_state()->mark_in_progress(); + bool mark_in_progress = _g1h->collector_state()->is_in_marking(); if (allocated_before_mark_start && mark_in_progress) { return false; @@ -530,7 +530,7 @@ void G1YoungCollector::pre_evacuate_collection_set(G1EvacInfo* evacuation_info) // Needs log buffers flushed. calculate_collection_set(evacuation_info, policy()->max_pause_time_ms()); - if (collector_state()->in_concurrent_start_gc()) { + if (collector_state()->is_in_concurrent_start_gc()) { Ticks start = Ticks::now(); concurrent_mark()->pre_concurrent_start(_gc_cause); phase_times()->record_prepare_concurrent_task_time_ms((Ticks::now() - start).seconds() * 1000.0); @@ -896,17 +896,10 @@ public: assert(obj != nullptr, "the caller should have filtered out null values"); const G1HeapRegionAttr region_attr =_g1h->region_attr(obj); - if (!region_attr.is_in_cset_or_humongous_candidate()) { - return; - } + assert(!region_attr.is_humongous_candidate(), "Humongous candidates should never be considered alive"); if (region_attr.is_in_cset()) { assert(obj->is_forwarded(), "invariant" ); *p = obj->forwardee(); - } else { - assert(!obj->is_forwarded(), "invariant" ); - assert(region_attr.is_humongous_candidate(), - "Only allowed G1HeapRegionAttr state is IsHumongous, but is %d", region_attr.type()); - _g1h->set_humongous_is_live(obj); } } }; @@ -932,7 +925,8 @@ public: template void do_oop_work(T* p) { oop obj = RawAccess<>::oop_load(p); - if (_g1h->is_in_cset_or_humongous_candidate(obj)) { + assert(!_g1h->region_attr(obj).is_humongous_candidate(), "Humongous candidates should never be considered alive"); + if (_g1h->is_in_cset(obj)) { // If the referent object has been forwarded (either copied // to a new location or to itself in the event of an // evacuation failure) then we need to update the reference @@ -1043,7 +1037,7 @@ void G1YoungCollector::post_evacuate_cleanup_2(G1ParScanThreadStateSet* per_thre } void G1YoungCollector::enqueue_candidates_as_root_regions() { - assert(collector_state()->in_concurrent_start_gc(), "must be"); + assert(collector_state()->is_in_concurrent_start_gc(), "must be"); G1CollectionSetCandidates* candidates = collection_set()->candidates(); candidates->iterate_regions([&] (G1HeapRegion* r) { @@ -1070,6 +1064,7 @@ void G1YoungCollector::post_evacuate_collection_set(G1EvacInfo* evacuation_info, allocator()->release_gc_alloc_regions(evacuation_info); #if TASKQUEUE_STATS + _g1h->task_queues()->print_and_reset_taskqueue_stats("Young GC"); // Logging uses thread states, which are deleted by cleanup, so this must // be done before cleanup. per_thread_states->print_partial_array_task_stats(); @@ -1082,7 +1077,7 @@ void G1YoungCollector::post_evacuate_collection_set(G1EvacInfo* evacuation_info, // Regions in the collection set candidates are roots for the marking (they are // not marked through considering they are very likely to be reclaimed soon. // They need to be enqueued explicitly compared to survivor regions. - if (collector_state()->in_concurrent_start_gc()) { + if (collector_state()->is_in_concurrent_start_gc()) { enqueue_candidates_as_root_regions(); } @@ -1185,9 +1180,8 @@ void G1YoungCollector::collect() { // Need to report the collection pause now since record_collection_pause_end() // modifies it to the next state. - jtm.report_pause_type(collector_state()->young_gc_pause_type(_concurrent_operation_is_full_mark)); + jtm.report_pause_type(collector_state()->gc_pause_type(_concurrent_operation_is_full_mark)); policy()->record_young_collection_end(_concurrent_operation_is_full_mark, evacuation_alloc_failed(), _allocation_word_size); } - TASKQUEUE_STATS_ONLY(_g1h->task_queues()->print_and_reset_taskqueue_stats("Oop Queue");) } diff --git a/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp index 75cff2b339b..2291a755cd3 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -61,9 +61,9 @@ void G1YoungGCAllocationFailureInjector::select_allocation_failure_regions() { bool G1YoungGCAllocationFailureInjector::arm_if_needed_for_gc_type(bool for_young_only_phase, bool during_concurrent_start, - bool mark_or_rebuild_in_progress) { + bool in_concurrent_cycle) { bool res = false; - if (mark_or_rebuild_in_progress) { + if (in_concurrent_cycle) { res |= G1GCAllocationFailureALotDuringConcMark; } if (during_concurrent_start) { @@ -89,14 +89,14 @@ void G1YoungGCAllocationFailureInjector::arm_if_needed() { // Now check if evacuation failure injection should be enabled for the current GC. G1CollectorState* collector_state = g1h->collector_state(); - const bool in_young_only_phase = collector_state->in_young_only_phase(); - const bool in_concurrent_start_gc = collector_state->in_concurrent_start_gc(); - const bool mark_or_rebuild_in_progress = collector_state->mark_or_rebuild_in_progress(); + const bool in_young_only_phase = collector_state->is_in_young_only_phase(); + const bool in_concurrent_start_gc = collector_state->is_in_concurrent_start_gc(); + const bool in_concurrent_cycle = collector_state->is_in_concurrent_cycle(); _inject_allocation_failure_for_current_gc &= arm_if_needed_for_gc_type(in_young_only_phase, in_concurrent_start_gc, - mark_or_rebuild_in_progress); + in_concurrent_cycle); if (_inject_allocation_failure_for_current_gc) { select_allocation_failure_regions(); diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index a0013d27172..3d49b8f025b 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp @@ -395,7 +395,7 @@ public: { ResourceMark rm; bool allocated_after_mark_start = r->bottom() == _g1h->concurrent_mark()->top_at_mark_start(r); - bool mark_in_progress = _g1h->collector_state()->mark_in_progress(); + bool mark_in_progress = _g1h->collector_state()->is_in_marking(); guarantee(obj->is_typeArray() || (allocated_after_mark_start || !mark_in_progress), "Only eagerly reclaiming primitive arrays is supported, other humongous objects only if allocated after mark start, but the object " PTR_FORMAT " (%s) is not (mark %d allocated after mark: %d).", @@ -501,7 +501,7 @@ class G1PostEvacuateCollectionSetCleanupTask2::ProcessEvacuationFailedRegionsTas // Concurrent mark does not mark through regions that we retain (they are root // regions wrt to marking), so we must clear their mark data (tams, bitmap, ...) // set eagerly or during evacuation failure. - bool clear_mark_data = !g1h->collector_state()->in_concurrent_start_gc() || + bool clear_mark_data = !g1h->collector_state()->is_in_concurrent_start_gc() || g1h->policy()->should_retain_evac_failed_region(r); if (clear_mark_data) { diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp index 4b8bca430f8..36de38c34bb 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp @@ -26,6 +26,7 @@ #define SHARE_GC_PARALLEL_MUTABLENUMASPACE_HPP #include "gc/parallel/mutableSpace.hpp" +#include "gc/shared/gc_globals.hpp" #include "gc/shared/gcUtil.hpp" #include "runtime/globals.hpp" #include "utilities/growableArray.hpp" diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index f49419595e1..b77294a2ac1 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -272,7 +272,7 @@ HeapWord* ParallelScavengeHeap::mem_allocate(size_t size) { HeapWord* ParallelScavengeHeap::mem_allocate_cas_noexpand(size_t size, bool is_tlab) { // Try young-gen first. - HeapWord* result = young_gen()->allocate(size); + HeapWord* result = young_gen()->cas_allocate(size); if (result != nullptr) { return result; } @@ -932,7 +932,7 @@ void ParallelScavengeHeap::resize_after_full_gc() { } HeapWord* ParallelScavengeHeap::allocate_loaded_archive_space(size_t size) { - return _old_gen->allocate(size); + return _old_gen->cas_allocate_with_expansion(size); } void ParallelScavengeHeap::complete_loaded_archive_space(MemRegion archive_space) { diff --git a/src/hotspot/share/gc/parallel/parallel_globals.hpp b/src/hotspot/share/gc/parallel/parallel_globals.hpp index 64e2effdeae..ba4a79f9254 100644 --- a/src/hotspot/share/gc/parallel/parallel_globals.hpp +++ b/src/hotspot/share/gc/parallel/parallel_globals.hpp @@ -31,6 +31,19 @@ product_pd, \ range, \ constraint) \ + product(uintx, NUMAChunkResizeWeight, 20, \ + "Percentage (0-100) used to weight the current sample when " \ + "computing exponentially decaying average for " \ + "AdaptiveNUMAChunkSizing") \ + range(0, 100) \ + \ + product(size_t, NUMASpaceResizeRate, 1*G, \ + "Do not reallocate more than this amount per collection") \ + range(0, max_uintx) \ + \ + product(bool, UseAdaptiveNUMAChunkSizing, true, \ + "Enable adaptive chunk sizing for NUMA") \ + \ product(bool, UseMaximumCompactionOnSystemGC, true, \ "Use maximum compaction in the Parallel Old garbage collector " \ "for a system GC") diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.cpp b/src/hotspot/share/gc/parallel/psCompactionManager.cpp index b8ea47eeb09..0108f1a9762 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.cpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.cpp @@ -203,13 +203,13 @@ void ParCompactionManager::remove_all_shadow_regions() { #if TASKQUEUE_STATS void ParCompactionManager::print_and_reset_taskqueue_stats() { - marking_stacks()->print_and_reset_taskqueue_stats("Marking Stacks"); + marking_stacks()->print_and_reset_taskqueue_stats("Full GC"); auto get_pa_stats = [&](uint i) { return _manager_array[i]->partial_array_task_stats(); }; PartialArrayTaskStats::log_set(ParallelGCThreads, get_pa_stats, - "Partial Array Task Stats"); + "Full GC Partial Array"); uint parallel_gc_threads = ParallelScavengeHeap::heap()->workers().max_workers(); for (uint i = 0; i < parallel_gc_threads; ++i) { get_pa_stats(i)->reset(); diff --git a/src/hotspot/share/gc/parallel/psOldGen.hpp b/src/hotspot/share/gc/parallel/psOldGen.hpp index 7e3975036d4..c8e6ada3ebd 100644 --- a/src/hotspot/share/gc/parallel/psOldGen.hpp +++ b/src/hotspot/share/gc/parallel/psOldGen.hpp @@ -110,7 +110,7 @@ class PSOldGen : public CHeapObj { void shrink(size_t bytes); // Used by GC-workers during GC or for CDS at startup. - HeapWord* allocate(size_t word_size) { + HeapWord* cas_allocate_with_expansion(size_t word_size) { HeapWord* res; do { res = cas_allocate_noexpand(word_size); diff --git a/src/hotspot/share/gc/parallel/psPromotionManager.cpp b/src/hotspot/share/gc/parallel/psPromotionManager.cpp index d6208755374..39fcc5556c6 100644 --- a/src/hotspot/share/gc/parallel/psPromotionManager.cpp +++ b/src/hotspot/share/gc/parallel/psPromotionManager.cpp @@ -138,13 +138,13 @@ bool PSPromotionManager::post_scavenge(YoungGCTracer& gc_tracer) { #if TASKQUEUE_STATS void PSPromotionManager::print_and_reset_taskqueue_stats() { - stack_array_depth()->print_and_reset_taskqueue_stats("Oop Queue"); + stack_array_depth()->print_and_reset_taskqueue_stats("Young GC"); auto get_pa_stats = [&](uint i) { return manager_array(i)->partial_array_task_stats(); }; PartialArrayTaskStats::log_set(ParallelGCThreads, get_pa_stats, - "Partial Array Task Stats"); + "Young GC Partial Array"); for (uint i = 0; i < ParallelGCThreads; ++i) { get_pa_stats(i)->reset(); } diff --git a/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp b/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp index 9e904e44b22..68370a33a54 100644 --- a/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp +++ b/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp @@ -204,13 +204,13 @@ inline HeapWord* PSPromotionManager::allocate_in_old_gen(Klass* klass, // Do we allocate directly, or flush and refill? if (obj_size > (OldPLABSize / 2)) { // Allocate this object directly - result = old_gen()->allocate(obj_size); + result = old_gen()->cas_allocate_with_expansion(obj_size); promotion_trace_event(cast_to_oop(result), klass, obj_size, age, true, nullptr); } else { // Flush and fill _old_lab.flush(); - HeapWord* lab_base = old_gen()->allocate(OldPLABSize); + HeapWord* lab_base = old_gen()->cas_allocate_with_expansion(OldPLABSize); if (lab_base != nullptr) { _old_lab.initialize(MemRegion(lab_base, OldPLABSize)); // Try the old lab allocation again. diff --git a/src/hotspot/share/gc/parallel/psYoungGen.hpp b/src/hotspot/share/gc/parallel/psYoungGen.hpp index 10aa037da98..ed10806ac99 100644 --- a/src/hotspot/share/gc/parallel/psYoungGen.hpp +++ b/src/hotspot/share/gc/parallel/psYoungGen.hpp @@ -128,7 +128,7 @@ class PSYoungGen : public CHeapObj { size_t max_gen_size() const { return _max_gen_size; } // Allocation - HeapWord* allocate(size_t word_size) { + HeapWord* cas_allocate(size_t word_size) { HeapWord* result = eden_space()->cas_allocate(word_size); return result; } diff --git a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp index 53577bad1d8..a888ea81707 100644 --- a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -758,8 +758,8 @@ Node* BarrierSetC2::obj_allocate(PhaseMacroExpand* macro, Node* mem, Node* toobi assert(UseTLAB, "Only for TLAB enabled allocations"); Node* thread = macro->transform_later(new ThreadLocalNode()); - Node* tlab_top_adr = macro->basic_plus_adr(macro->top()/*not oop*/, thread, in_bytes(JavaThread::tlab_top_offset())); - Node* tlab_end_adr = macro->basic_plus_adr(macro->top()/*not oop*/, thread, in_bytes(JavaThread::tlab_end_offset())); + Node* tlab_top_adr = macro->off_heap_plus_addr(thread, in_bytes(JavaThread::tlab_top_offset())); + Node* tlab_end_adr = macro->off_heap_plus_addr(thread, in_bytes(JavaThread::tlab_end_offset())); // Load TLAB end. // @@ -778,7 +778,7 @@ Node* BarrierSetC2::obj_allocate(PhaseMacroExpand* macro, Node* mem, Node* toobi macro->transform_later(old_tlab_top); // Add to heap top to get a new TLAB top - Node* new_tlab_top = new AddPNode(macro->top(), old_tlab_top, size_in_bytes); + Node* new_tlab_top = AddPNode::make_off_heap(old_tlab_top, size_in_bytes); macro->transform_later(new_tlab_top); // Check against TLAB end @@ -813,7 +813,10 @@ Node* BarrierSetC2::obj_allocate(PhaseMacroExpand* macro, Node* mem, Node* toobi return old_tlab_top; } -static const TypeFunc* clone_type() { +const TypeFunc* BarrierSetC2::_clone_type_Type = nullptr; + +void BarrierSetC2::make_clone_type() { + assert(BarrierSetC2::_clone_type_Type == nullptr, "should be"); // Create input type (domain) int argcnt = NOT_LP64(3) LP64_ONLY(4); const Type** const domain_fields = TypeTuple::fields(argcnt); @@ -829,7 +832,12 @@ static const TypeFunc* clone_type() { const Type** const range_fields = TypeTuple::fields(0); const TypeTuple* const range = TypeTuple::make(TypeFunc::Parms + 0, range_fields); - return TypeFunc::make(domain, range); + BarrierSetC2::_clone_type_Type = TypeFunc::make(domain, range); +} + +inline const TypeFunc* BarrierSetC2::clone_type() { + assert(BarrierSetC2::_clone_type_Type != nullptr, "should be initialized"); + return BarrierSetC2::_clone_type_Type; } #define XTOP LP64_ONLY(COMMA phase->top()) diff --git a/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp index 7b9cb985cff..a486a88c48f 100644 --- a/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp @@ -270,6 +270,9 @@ public: // various GC barrier sets inherit from the BarrierSetC2 class to sprinkle // barriers into the accesses. class BarrierSetC2: public CHeapObj { +private: + static const TypeFunc* _clone_type_Type; + protected: virtual void resolve_address(C2Access& access) const; virtual Node* store_at_resolved(C2Access& access, C2AccessValue& val) const; @@ -379,6 +382,9 @@ public: static int arraycopy_payload_base_offset(bool is_array); + static void make_clone_type(); + static const TypeFunc* clone_type(); + #ifndef PRODUCT virtual void dump_barrier_data(const MachNode* mach, outputStream* st) const { st->print("%x", mach->barrier_data()); diff --git a/src/hotspot/share/gc/shared/gc_globals.hpp b/src/hotspot/share/gc/shared/gc_globals.hpp index 2fd062de648..66ca10f1fb6 100644 --- a/src/hotspot/share/gc/shared/gc_globals.hpp +++ b/src/hotspot/share/gc/shared/gc_globals.hpp @@ -256,12 +256,6 @@ "before pushing a continuation entry") \ range(1, INT_MAX/2) \ \ - product_pd(bool, NeverActAsServerClassMachine, \ - "(Deprecated) Never act like a server-class machine") \ - \ - product(bool, AlwaysActAsServerClassMachine, false, \ - "(Deprecated) Always act like a server-class machine") \ - \ product(bool, AggressiveHeap, false, \ "(Deprecated) Optimize heap options for long-running memory " \ "intensive apps") \ diff --git a/src/hotspot/share/gc/shared/partialArrayTaskStats.cpp b/src/hotspot/share/gc/shared/partialArrayTaskStats.cpp index ac8a380ec9a..090430963c6 100644 --- a/src/hotspot/share/gc/shared/partialArrayTaskStats.cpp +++ b/src/hotspot/share/gc/shared/partialArrayTaskStats.cpp @@ -64,7 +64,7 @@ static const char* const stats_hdr[] = { }; void PartialArrayTaskStats::print_header(outputStream* s, const char* title) { - s->print_cr("%s:", title); + s->print_cr("GC Task Stats %s", title); for (uint i = 0; i < ARRAY_SIZE(stats_hdr); ++i) { s->print_cr("%s", stats_hdr[i]); } diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp index 61cf73fe04a..4c160929f5a 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp @@ -37,7 +37,6 @@ #include "utilities/copy.hpp" size_t ThreadLocalAllocBuffer::_max_size = 0; -int ThreadLocalAllocBuffer::_reserve_for_allocation_prefetch = 0; unsigned int ThreadLocalAllocBuffer::_target_refills = 0; ThreadLocalAllocBuffer::ThreadLocalAllocBuffer() : @@ -225,30 +224,6 @@ void ThreadLocalAllocBuffer::startup_initialization() { // abort during VM initialization. _target_refills = MAX2(_target_refills, 2U); -#ifdef COMPILER2 - // If the C2 compiler is present, extra space is needed at the end of - // TLABs, otherwise prefetching instructions generated by the C2 - // compiler will fault (due to accessing memory outside of heap). - // The amount of space is the max of the number of lines to - // prefetch for array and for instance allocations. (Extra space must be - // reserved to accommodate both types of allocations.) - // - // Only SPARC-specific BIS instructions are known to fault. (Those - // instructions are generated if AllocatePrefetchStyle==3 and - // AllocatePrefetchInstr==1). To be on the safe side, however, - // extra space is reserved for all combinations of - // AllocatePrefetchStyle and AllocatePrefetchInstr. - // - // If the C2 compiler is not present, no space is reserved. - - // +1 for rounding up to next cache line, +1 to be safe - if (CompilerConfig::is_c2_or_jvmci_compiler_enabled()) { - int lines = MAX2(AllocatePrefetchLines, AllocateInstancePrefetchLines) + 2; - _reserve_for_allocation_prefetch = (AllocatePrefetchDistance + AllocatePrefetchStepSize * lines) / - (int)HeapWordSize; - } -#endif - // During jvm startup, the main thread is initialized // before the heap is initialized. So reinitialize it now. guarantee(Thread::current()->is_Java_thread(), "tlab initialization thread not Java thread"); @@ -454,8 +429,7 @@ void ThreadLocalAllocStats::publish() { } size_t ThreadLocalAllocBuffer::end_reserve() { - size_t reserve_size = CollectedHeap::lab_alignment_reserve(); - return MAX2(reserve_size, (size_t)_reserve_for_allocation_prefetch); + return CollectedHeap::lab_alignment_reserve(); } size_t ThreadLocalAllocBuffer::estimated_used_bytes() const { diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp index 25d9bf00eac..eb664d13961 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp @@ -57,7 +57,6 @@ private: uint64_t _allocated_before_last_gc; // total bytes allocated up until the last gc static size_t _max_size; // maximum size of any TLAB - static int _reserve_for_allocation_prefetch; // Reserve at the end of the TLAB static unsigned _target_refills; // expected number of refills between GCs unsigned _number_of_refills; diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp index fdfde866cd7..f721c3cd001 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Red Hat, Inc. All rights reserved. + * Copyright (c) 2018, 2026, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -519,7 +519,33 @@ void ShenandoahBarrierSetC2::post_barrier(GraphKit* kit, #undef __ -const TypeFunc* ShenandoahBarrierSetC2::write_barrier_pre_Type() { +const TypeFunc* ShenandoahBarrierSetC2::_write_barrier_pre_Type = nullptr; +const TypeFunc* ShenandoahBarrierSetC2::_clone_barrier_Type = nullptr; +const TypeFunc* ShenandoahBarrierSetC2::_load_reference_barrier_Type = nullptr; + +inline const TypeFunc* ShenandoahBarrierSetC2::write_barrier_pre_Type() { + assert(ShenandoahBarrierSetC2::_write_barrier_pre_Type != nullptr, "should be initialized"); + return ShenandoahBarrierSetC2::_write_barrier_pre_Type; +} + +inline const TypeFunc* ShenandoahBarrierSetC2::clone_barrier_Type() { + assert(ShenandoahBarrierSetC2::_clone_barrier_Type != nullptr, "should be initialized"); + return ShenandoahBarrierSetC2::_clone_barrier_Type; +} + +const TypeFunc* ShenandoahBarrierSetC2::load_reference_barrier_Type() { + assert(ShenandoahBarrierSetC2::_load_reference_barrier_Type != nullptr, "should be initialized"); + return ShenandoahBarrierSetC2::_load_reference_barrier_Type; +} + +void ShenandoahBarrierSetC2::init() { + ShenandoahBarrierSetC2::make_write_barrier_pre_Type(); + ShenandoahBarrierSetC2::make_clone_barrier_Type(); + ShenandoahBarrierSetC2::make_load_reference_barrier_Type(); +} + +void ShenandoahBarrierSetC2::make_write_barrier_pre_Type() { + assert(ShenandoahBarrierSetC2::_write_barrier_pre_Type == nullptr, "should be"); const Type **fields = TypeTuple::fields(1); fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // original field value const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+1, fields); @@ -528,10 +554,11 @@ const TypeFunc* ShenandoahBarrierSetC2::write_barrier_pre_Type() { fields = TypeTuple::fields(0); const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0, fields); - return TypeFunc::make(domain, range); + ShenandoahBarrierSetC2::_write_barrier_pre_Type = TypeFunc::make(domain, range); } -const TypeFunc* ShenandoahBarrierSetC2::clone_barrier_Type() { +void ShenandoahBarrierSetC2::make_clone_barrier_Type() { + assert(ShenandoahBarrierSetC2::_clone_barrier_Type == nullptr, "should be"); const Type **fields = TypeTuple::fields(1); fields[TypeFunc::Parms+0] = TypeOopPtr::NOTNULL; // src oop const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+1, fields); @@ -540,10 +567,11 @@ const TypeFunc* ShenandoahBarrierSetC2::clone_barrier_Type() { fields = TypeTuple::fields(0); const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0, fields); - return TypeFunc::make(domain, range); + ShenandoahBarrierSetC2::_clone_barrier_Type = TypeFunc::make(domain, range); } -const TypeFunc* ShenandoahBarrierSetC2::load_reference_barrier_Type() { +void ShenandoahBarrierSetC2::make_load_reference_barrier_Type() { + assert(ShenandoahBarrierSetC2::_load_reference_barrier_Type == nullptr, "should be"); const Type **fields = TypeTuple::fields(2); fields[TypeFunc::Parms+0] = TypeOopPtr::BOTTOM; // original field value fields[TypeFunc::Parms+1] = TypeRawPtr::BOTTOM; // original load address @@ -555,7 +583,7 @@ const TypeFunc* ShenandoahBarrierSetC2::load_reference_barrier_Type() { fields[TypeFunc::Parms+0] = TypeOopPtr::BOTTOM; const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+1, fields); - return TypeFunc::make(domain, range); + ShenandoahBarrierSetC2::_load_reference_barrier_Type = TypeFunc::make(domain, range); } Node* ShenandoahBarrierSetC2::store_at_resolved(C2Access& access, C2AccessValue& val) const { @@ -890,7 +918,7 @@ void ShenandoahBarrierSetC2::clone_at_expansion(PhaseMacroExpand* phase, ArrayCo Node* thread = phase->transform_later(new ThreadLocalNode()); Node* offset = phase->igvn().MakeConX(in_bytes(ShenandoahThreadLocalData::gc_state_offset())); - Node* gc_state_addr = phase->transform_later(new AddPNode(phase->C->top(), thread, offset)); + Node* gc_state_addr = phase->transform_later(AddPNode::make_off_heap(thread, offset)); uint gc_state_idx = Compile::AliasIdxRaw; const TypePtr* gc_state_adr_type = nullptr; // debug-mode-only argument diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp index dd9e9bcc1a5..108eaa0998b 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp @@ -82,6 +82,13 @@ private: static bool clone_needs_barrier(Node* src, PhaseGVN& gvn); + static const TypeFunc* _write_barrier_pre_Type; + static const TypeFunc* _clone_barrier_Type; + static const TypeFunc* _load_reference_barrier_Type; + static void make_write_barrier_pre_Type(); + static void make_clone_barrier_Type(); + static void make_load_reference_barrier_Type(); + protected: virtual Node* load_at_resolved(C2Access& access, const Type* val_type) const; virtual Node* store_at_resolved(C2Access& access, C2AccessValue& val) const; @@ -106,6 +113,8 @@ public: static const TypeFunc* write_barrier_pre_Type(); static const TypeFunc* clone_barrier_Type(); static const TypeFunc* load_reference_barrier_Type(); + static void init(); + virtual bool has_load_barrier_nodes() const { return true; } // This is the entry-point for the backend to perform accesses through the Access API. diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index 40fe0c00490..015276feb5c 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021, Red Hat, Inc. All rights reserved. + * Copyright (c) 2015, 2026, Red Hat, Inc. All rights reserved. * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -871,7 +871,7 @@ void ShenandoahBarrierC2Support::test_gc_state(Node*& ctrl, Node* raw_mem, Node* Node* thread = new ThreadLocalNode(); Node* gc_state_offset = igvn.MakeConX(in_bytes(ShenandoahThreadLocalData::gc_state_offset())); - Node* gc_state_addr = new AddPNode(phase->C->top(), thread, gc_state_offset); + Node* gc_state_addr = AddPNode::make_off_heap(thread, gc_state_offset); Node* gc_state = new LoadBNode(old_ctrl, raw_mem, gc_state_addr, DEBUG_ONLY(phase->C->get_adr_type(Compile::AliasIdxRaw)) NOT_DEBUG(nullptr), TypeInt::BYTE, MemNode::unordered); diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp index ac8b3ebdf37..6b327d7e4af 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp @@ -273,7 +273,7 @@ void ShenandoahAdaptiveHeuristics::add_gc_time(double timestamp, double gc_time) } double ShenandoahAdaptiveHeuristics::predict_gc_time(double timestamp_at_start) { - return _gc_time_m * timestamp_at_start + _gc_time_b + _gc_time_sd * _margin_of_error_sd;; + return _gc_time_m * timestamp_at_start + _gc_time_b + _gc_time_sd * _margin_of_error_sd; } void ShenandoahAdaptiveHeuristics::add_rate_to_acceleration_history(double timestamp, double rate) { @@ -739,7 +739,7 @@ size_t ShenandoahAdaptiveHeuristics::accelerated_consumption(double& acceleratio if (i > 0) { // first sample not included in weighted average because it has no weight. double sample_weight = x_array[i] - x_array[i-1]; - weighted_y_sum = y_array[i] * sample_weight; + weighted_y_sum += y_array[i] * sample_weight; total_weight += sample_weight; } y_sum += y_array[i]; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp index f3d31b8d0e1..08d628e67ff 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp @@ -25,7 +25,6 @@ #include "gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp" #include "gc/shenandoah/shenandoahCollectionSet.hpp" -#include "gc/shenandoah/shenandoahCollectionSetPreselector.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.inline.hpp" @@ -76,25 +75,16 @@ ShenandoahGenerationalHeuristics::ShenandoahGenerationalHeuristics(ShenandoahGen } void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectionSet* collection_set) { - ShenandoahHeap* heap = ShenandoahHeap::heap(); + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + + assert(collection_set->is_empty(), "Collection set must be empty here"); _add_regions_to_old = 0; - // Seed the collection set with resource area-allocated - // preselected regions, which are removed when we exit this scope. - ShenandoahCollectionSetPreselector preselector(collection_set, heap->num_regions()); - - // Find the amount that will be promoted, regions that will be promoted in - // place, and preselected older regions that will be promoted by evacuation. - compute_evacuation_budgets(heap); - - // Choose the collection set, including the regions preselected above for promotion into the old generation. + // Choose the collection set filter_regions(collection_set); - // Even if collection_set->is_empty(), we want to adjust budgets, making reserves available to mutator. - adjust_evacuation_budgets(heap, collection_set); - - if (_generation->is_global()) { + if (!collection_set->is_empty() && _generation->is_global()) { // We have just chosen a collection set for a global cycle. The mark bitmap covering old regions is complete, so // the remembered set scan can use that to avoid walking into garbage. When the next old mark begins, we will // use the mark bitmap to make the old regions parsable by coalescing and filling any unmarked objects. Thus, @@ -108,7 +98,8 @@ void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectio } } -void ShenandoahGenerationalHeuristics::compute_evacuation_budgets(ShenandoahHeap* const heap) { +void ShenandoahGenerationalHeuristics::compute_evacuation_budgets(ShenandoahInPlacePromotionPlanner& in_place_promotions, + ShenandoahHeap* const heap) { shenandoah_assert_generational(); ShenandoahOldGeneration* const old_generation = heap->old_generation(); @@ -206,7 +197,7 @@ void ShenandoahGenerationalHeuristics::compute_evacuation_budgets(ShenandoahHeap // If is_global(), we let garbage-first heuristic determine cset membership. Otherwise, we give priority // to tenurable regions by preselecting regions for promotion by evacuation (obtaining the live data to seed promoted_reserve). // This also identifies regions that will be promoted in place. These use the tenuring threshold. - const size_t consumed_by_advance_promotion = select_aged_regions(_generation->is_global()? 0: old_promo_reserve); + const size_t consumed_by_advance_promotion = select_aged_regions(in_place_promotions, _generation->is_global()? 0: old_promo_reserve); assert(consumed_by_advance_promotion <= old_promo_reserve, "Do not promote more than budgeted"); // The young evacuation reserve can be no larger than young_unaffiliated. Planning to evacuate into partially consumed @@ -231,23 +222,19 @@ void ShenandoahGenerationalHeuristics::compute_evacuation_budgets(ShenandoahHeap } void ShenandoahGenerationalHeuristics::filter_regions(ShenandoahCollectionSet* collection_set) { - assert(collection_set->is_empty(), "Must be empty"); - auto heap = ShenandoahGenerationalHeap::heap(); - size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); - + const size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); // Check all pinned regions have updated status before choosing the collection set. heap->assert_pinned_region_status(_generation); // Step 1. Build up the region candidates we care about, rejecting losers and accepting winners right away. - size_t num_regions = heap->num_regions(); + const size_t num_regions = heap->num_regions(); RegionData* candidates = _region_data; size_t cand_idx = 0; - size_t preselected_candidates = 0; size_t total_garbage = 0; @@ -257,23 +244,12 @@ void ShenandoahGenerationalHeuristics::filter_regions(ShenandoahCollectionSet* c size_t free = 0; size_t free_regions = 0; - // This counts number of humongous regions that we intend to promote in this cycle. - size_t humongous_regions_promoted = 0; - // This counts number of regular regions that will be promoted in place. - size_t regular_regions_promoted_in_place = 0; - // This counts bytes of memory used by regular regions to be promoted in place. - size_t regular_regions_promoted_usage = 0; - // This counts bytes of memory free in regular regions to be promoted in place. - size_t regular_regions_promoted_free = 0; - // This counts bytes of garbage memory in regular regions to be promoted in place. - size_t regular_regions_promoted_garbage = 0; - for (size_t i = 0; i < num_regions; i++) { ShenandoahHeapRegion* region = heap->get_region(i); if (!_generation->contains(region)) { continue; } - size_t garbage = region->garbage(); + const size_t garbage = region->garbage(); total_garbage += garbage; if (region->is_empty()) { free_regions++; @@ -285,106 +261,67 @@ void ShenandoahGenerationalHeuristics::filter_regions(ShenandoahCollectionSet* c immediate_garbage += garbage; region->make_trash_immediate(); } else { - bool is_candidate; - // This is our candidate for later consideration. - if (collection_set->is_preselected(i)) { - assert(heap->is_tenurable(region), "Preselection filter"); - is_candidate = true; - preselected_candidates++; - // Set garbage value to maximum value to force this into the sorted collection set. - garbage = region_size_bytes; - } else if (region->is_young() && heap->is_tenurable(region)) { - // Note that for GLOBAL GC, region may be OLD, and OLD regions do not qualify for pre-selection - - // This region is old enough to be promoted but it was not preselected, either because its garbage is below - // old garbage threshold so it will be promoted in place, or because there is not sufficient room - // in old gen to hold the evacuated copies of this region's live data. In both cases, we choose not to - // place this region into the collection set. - if (region->get_top_before_promote() != nullptr) { - // Region was included for promotion-in-place - regular_regions_promoted_in_place++; - regular_regions_promoted_usage += region->used_before_promote(); - regular_regions_promoted_free += region->free(); - regular_regions_promoted_garbage += region->garbage(); - } - is_candidate = false; - } else { - is_candidate = true; - } - if (is_candidate) { - candidates[cand_idx].set_region_and_garbage(region, garbage); - cand_idx++; - } + // This is our candidate for later consideration. Note that this region + // could still be promoted in place and may not necessarily end up in the + // collection set. + assert(region->get_top_before_promote() == nullptr, "Cannot add region %zu scheduled for in-place-promotion to the collection set", i); + candidates[cand_idx].set_region_and_garbage(region, garbage); + cand_idx++; } } else if (region->is_humongous_start()) { // Reclaim humongous regions here, and count them as the immediate garbage -#ifdef ASSERT - bool reg_live = region->has_live(); - bool bm_live = _generation->complete_marking_context()->is_marked(cast_to_oop(region->bottom())); - assert(reg_live == bm_live, - "Humongous liveness and marks should agree. Region live: %s; Bitmap live: %s; Region Live Words: %zu", - BOOL_TO_STR(reg_live), BOOL_TO_STR(bm_live), region->get_live_data_words()); -#endif + DEBUG_ONLY(assert_humongous_mark_consistency(region)); if (!region->has_live()) { heap->trash_humongous_region_at(region); // Count only the start. Continuations would be counted on "trash" path immediate_regions++; immediate_garbage += garbage; - } else { - if (region->is_young() && heap->is_tenurable(region)) { - oop obj = cast_to_oop(region->bottom()); - size_t humongous_regions = ShenandoahHeapRegion::required_regions(obj->size() * HeapWordSize); - humongous_regions_promoted += humongous_regions; - } } } else if (region->is_trash()) { - // Count in just trashed collection set, during coalesced CM-with-UR + // Count in just trashed humongous continuation regions immediate_regions++; immediate_garbage += garbage; } } - heap->old_generation()->set_expected_humongous_region_promotions(humongous_regions_promoted); - heap->old_generation()->set_expected_regular_region_promotions(regular_regions_promoted_in_place); - log_info(gc, ergo)("Planning to promote in place %zu humongous regions and %zu" - " regular regions, spanning a total of %zu used bytes", - humongous_regions_promoted, regular_regions_promoted_in_place, - humongous_regions_promoted * ShenandoahHeapRegion::region_size_bytes() + - regular_regions_promoted_usage); // Step 2. Look back at garbage statistics, and decide if we want to collect anything, // given the amount of immediately reclaimable garbage. If we do, figure out the collection set. + assert(immediate_garbage <= total_garbage, + "Cannot have more immediate garbage than total garbage: " PROPERFMT " vs " PROPERFMT, + PROPERFMTARGS(immediate_garbage), PROPERFMTARGS(total_garbage)); - assert (immediate_garbage <= total_garbage, - "Cannot have more immediate garbage than total garbage: %zu%s vs %zu%s", - byte_size_in_proper_unit(immediate_garbage), proper_unit_for_byte_size(immediate_garbage), - byte_size_in_proper_unit(total_garbage), proper_unit_for_byte_size(total_garbage)); + const size_t immediate_percent = (total_garbage == 0) ? 0 : (immediate_garbage * 100 / total_garbage); + ShenandoahInPlacePromotionPlanner in_place_promotions(heap); + if (immediate_percent <= ShenandoahImmediateThreshold) { - size_t immediate_percent = (total_garbage == 0) ? 0 : (immediate_garbage * 100 / total_garbage); - bool doing_promote_in_place = (humongous_regions_promoted + regular_regions_promoted_in_place > 0); + // Find the amount that will be promoted, regions that will be promoted in + // place, and preselected older regions that will be promoted by evacuation. + compute_evacuation_budgets(in_place_promotions, heap); - if (doing_promote_in_place || (preselected_candidates > 0) || (immediate_percent <= ShenandoahImmediateThreshold)) { // Call the subclasses to add young-gen regions into the collection set. choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free); - } - if (collection_set->has_old_regions()) { - heap->shenandoah_policy()->record_mixed_cycle(); + // Even if collection_set->is_empty(), we want to adjust budgets, making reserves available to mutator. + adjust_evacuation_budgets(heap, collection_set); + + if (collection_set->has_old_regions()) { + heap->shenandoah_policy()->record_mixed_cycle(); + } } collection_set->summarize(total_garbage, immediate_garbage, immediate_regions); - ShenandoahTracer::report_evacuation_info(collection_set, free_regions, - humongous_regions_promoted, - regular_regions_promoted_in_place, - regular_regions_promoted_garbage, - regular_regions_promoted_free, + in_place_promotions.humongous_region_stats().count, + in_place_promotions.regular_region_stats().count, + in_place_promotions.regular_region_stats().garbage, + in_place_promotions.regular_region_stats().free, immediate_regions, immediate_garbage); } -// Preselect for inclusion into the collection set all regions whose age is at or above tenure age and for which the +// Select for inclusion into the collection set all regions whose age is at or above tenure age and for which the // garbage percentage exceeds a dynamically adjusted threshold (known as the old-garbage threshold percentage). We // identify these regions by setting the appropriate entry of the collection set's preselected regions array to true. // All entries are initialized to false before calling this function. @@ -400,15 +337,13 @@ void ShenandoahGenerationalHeuristics::filter_regions(ShenandoahCollectionSet* c // that this allows us to more accurately budget memory to hold the results of evacuation. Memory for evacuation // of aged regions must be reserved in the old generation. Memory for evacuation of all other regions must be // reserved in the young generation. -size_t ShenandoahGenerationalHeuristics::select_aged_regions(const size_t old_promotion_reserve) { +size_t ShenandoahGenerationalHeuristics::select_aged_regions(ShenandoahInPlacePromotionPlanner& in_place_promotions, + const size_t old_promotion_reserve) { // There should be no regions configured for subsequent in-place-promotions carried over from the previous cycle. assert_no_in_place_promotions(); auto const heap = ShenandoahGenerationalHeap::heap(); - ShenandoahFreeSet* free_set = heap->free_set(); - bool* const candidate_regions_for_promotion_by_copy = heap->collection_set()->preselected_regions(); - ShenandoahMarkingContext* const ctx = heap->marking_context(); size_t promo_potential = 0; size_t candidates = 0; @@ -421,14 +356,21 @@ size_t ShenandoahGenerationalHeuristics::select_aged_regions(const size_t old_pr ResourceMark rm; AgedRegionData* sorted_regions = NEW_RESOURCE_ARRAY(AgedRegionData, num_regions); - ShenandoahInPlacePromotionPlanner in_place_promotions(heap); - for (idx_t i = 0; i < num_regions; i++) { ShenandoahHeapRegion* const r = heap->get_region(i); - if (r->is_empty() || !r->has_live() || !r->is_young() || !r->is_regular()) { - // skip over regions that aren't regular young with some live data + if (r->is_empty() || !r->has_live() || !r->is_young()) { + // skip over regions that aren't young with some live data continue; } + + if (!r->is_regular()) { + if (r->is_humongous_start() && heap->is_tenurable(r)) { + in_place_promotions.prepare(r); + } + // Nothing else to be done for humongous regions + continue; + } + if (heap->is_tenurable(r)) { if (in_place_promotions.is_eligible(r)) { // We prefer to promote this region in place because it has a small amount of garbage and a large usage. @@ -470,7 +412,7 @@ size_t ShenandoahGenerationalHeuristics::select_aged_regions(const size_t old_pr // Subsequent regions may be selected if they have smaller live data. } - in_place_promotions.update_free_set(); + in_place_promotions.complete_planning(); // Sort in increasing order according to live data bytes. Note that candidates represents the number of regions // that qualify to be promoted by evacuation. @@ -485,14 +427,14 @@ size_t ShenandoahGenerationalHeuristics::select_aged_regions(const size_t old_pr const size_t promotion_need = (size_t) (region_live_data * ShenandoahPromoEvacWaste); if (old_consumed + promotion_need <= old_promotion_reserve) { old_consumed += promotion_need; - candidate_regions_for_promotion_by_copy[region->index()] = true; + heap->collection_set()->add_region(region); selected_regions++; selected_live += region_live_data; } else { // We rejected this promotable region from the collection set because we had no room to hold its copy. // Add this region to promo potential for next GC. promo_potential += region_live_data; - assert(!candidate_regions_for_promotion_by_copy[region->index()], "Shouldn't be selected"); + assert(!heap->collection_set()->is_in(region), "Region %zu shouldn't be in the collection set", region->index()); } // We keep going even if one region is excluded from selection because we need to accumulate all eligible // regions that are not preselected into promo_potential @@ -556,12 +498,12 @@ void ShenandoahGenerationalHeuristics::adjust_evacuation_budgets(ShenandoahHeap* size_t young_evacuated = collection_set->get_live_bytes_in_untenurable_regions(); size_t young_evacuated_reserve_used = (size_t) (ShenandoahEvacWaste * double(young_evacuated)); - size_t total_young_available = young_generation->available_with_reserve() - _add_regions_to_old * region_size_bytes;; + size_t total_young_available = young_generation->available_with_reserve() - _add_regions_to_old * region_size_bytes; assert(young_evacuated_reserve_used <= total_young_available, "Cannot evacuate (%zu) more than is available in young (%zu)", young_evacuated_reserve_used, total_young_available); young_generation->set_evacuation_reserve(young_evacuated_reserve_used); - // We have not yet rebuilt the free set. Some of the memory that is thought to be avaiable within old may no + // We have not yet rebuilt the free set. Some of the memory that is thought to be available within old may no // longer be available if that memory had been free within regions that were selected for the collection set. // Make the necessary adjustments to old_available. size_t old_available = @@ -640,24 +582,3 @@ void ShenandoahGenerationalHeuristics::adjust_evacuation_budgets(ShenandoahHeap* old_generation->set_promoted_reserve(total_promotion_reserve); old_generation->reset_promoted_expended(); } - -size_t ShenandoahGenerationalHeuristics::add_preselected_regions_to_collection_set(ShenandoahCollectionSet* cset, - const RegionData* data, - size_t size) const { - // cur_young_garbage represents the amount of memory to be reclaimed from young-gen. In the case that live objects - // are known to be promoted out of young-gen, we count this as cur_young_garbage because this memory is reclaimed - // from young-gen and becomes available to serve future young-gen allocation requests. - size_t cur_young_garbage = 0; - for (size_t idx = 0; idx < size; idx++) { - ShenandoahHeapRegion* r = data[idx].get_region(); - if (cset->is_preselected(r->index())) { - assert(ShenandoahGenerationalHeap::heap()->is_tenurable(r), "Preselected regions must have tenure age"); - // Entire region will be promoted, This region does not impact young-gen or old-gen evacuation reserve. - // This region has been pre-selected and its impact on promotion reserve is already accounted for. - cur_young_garbage += r->garbage(); - cset->add_region(r); - } - } - return cur_young_garbage; -} - diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp index da883d0d26f..c418e8c24ec 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp @@ -27,6 +27,7 @@ #include "gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp" +#include "gc/shenandoah/shenandoahInPlacePromoter.hpp" class ShenandoahGeneration; class ShenandoahHeap; @@ -53,7 +54,7 @@ public: private: // Compute evacuation budgets prior to choosing collection set. - void compute_evacuation_budgets(ShenandoahHeap* const heap); + void compute_evacuation_budgets(ShenandoahInPlacePromotionPlanner& in_place_promotions, ShenandoahHeap* const heap); // Preselect for possible inclusion into the collection set exactly the most // garbage-dense regions, including those that satisfy criteria 1 & 2 below, @@ -70,7 +71,7 @@ private: // regions, which are marked in the preselected_regions() indicator // array of the heap's collection set, which should be initialized // to false. - size_t select_aged_regions(const size_t old_promotion_reserve); + size_t select_aged_regions(ShenandoahInPlacePromotionPlanner& in_place_promotions, const size_t old_promotion_reserve); // Filter and sort remaining regions before adding to collection set. void filter_regions(ShenandoahCollectionSet* collection_set); @@ -84,10 +85,6 @@ protected: ShenandoahGeneration* _generation; size_t _add_regions_to_old; - - size_t add_preselected_regions_to_collection_set(ShenandoahCollectionSet* cset, - const RegionData* data, - size_t size) const; }; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp index dd2ad28aa4b..ed25cd2e1a9 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp @@ -31,21 +31,94 @@ #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "utilities/quickSort.hpp" +bool ShenandoahEvacuationBudget::try_reserve(size_t bytes) { + size_t new_consumption = _consumed + bytes; + if (new_consumption <= _reserve) { + return true; + } + // Try expanding from shared pool + size_t new_reserve = _reserve; + size_t new_committed = _shared->committed; + while ((new_consumption > new_reserve) && (new_committed < _shared->limit)) { + new_committed += _region_size_bytes; + new_reserve += _region_size_bytes; + } + if (new_consumption <= new_reserve) { + _reserve = new_reserve; + _shared->committed = new_committed; + return true; + } + return false; +} + +void ShenandoahEvacuationBudget::commit(size_t consumption, size_t live) { + _consumed += consumption; + _live_bytes += live; + _region_count++; +} + +ShenandoahGlobalRegionDisposition ShenandoahGlobalCSetBudget::try_add_region( + const ShenandoahGlobalRegionAttributes& region) { + + size_t region_garbage = region.garbage; + size_t new_garbage = _cur_garbage + region_garbage; + bool add_regardless = (region_garbage > _ignore_threshold) && (new_garbage < _min_garbage); + + if (!add_regardless && (region_garbage < _garbage_threshold)) { + return ShenandoahGlobalRegionDisposition::SKIP; + } + + size_t live_bytes = region.live_data_bytes; + + if (region.is_old) { + size_t evac_need = old_evac.anticipated_consumption(live_bytes); + size_t promo_loss = region.free_bytes; + + // Snapshot state for rollback — old branch does two reservations + size_t saved_committed = _shared.committed; + size_t saved_old_reserve = old_evac.reserve(); + size_t saved_promo_reserve = promo.reserve(); + + if (old_evac.try_reserve(evac_need) && promo.try_reserve(promo_loss)) { + old_evac.commit(evac_need, live_bytes); + promo.commit_raw(promo_loss); + _cur_garbage = new_garbage; + return ShenandoahGlobalRegionDisposition::ADD_OLD_EVAC; + } + _shared.committed = saved_committed; + old_evac.set_reserve(saved_old_reserve); + promo.set_reserve(saved_promo_reserve); + return ShenandoahGlobalRegionDisposition::SKIP; + } else if (region.is_tenurable) { + size_t promo_need = promo.anticipated_consumption(live_bytes); + if (promo.try_reserve(promo_need)) { + promo.commit(promo_need, live_bytes); + _cur_garbage = new_garbage; + return ShenandoahGlobalRegionDisposition::ADD_PROMO; + } + return ShenandoahGlobalRegionDisposition::SKIP; + } else { + size_t evac_need = young_evac.anticipated_consumption(live_bytes); + if (young_evac.try_reserve(evac_need)) { + young_evac.commit(evac_need, live_bytes); + _cur_garbage = new_garbage; + return ShenandoahGlobalRegionDisposition::ADD_YOUNG_EVAC; + } + return ShenandoahGlobalRegionDisposition::SKIP; + } +} + ShenandoahGlobalHeuristics::ShenandoahGlobalHeuristics(ShenandoahGlobalGeneration* generation) : ShenandoahGenerationalHeuristics(generation) { } - void ShenandoahGlobalHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, RegionData* data, size_t size, size_t actual_free) { - // Better select garbage-first regions QuickSort::sort(data, size, compare_by_garbage); - - choose_global_collection_set(cset, data, size, actual_free, 0 /* cur_young_garbage */); + choose_global_collection_set(cset, data, size, actual_free, 0); } - void ShenandoahGlobalHeuristics::choose_global_collection_set(ShenandoahCollectionSet* cset, const ShenandoahHeuristics::RegionData* data, size_t size, size_t actual_free, @@ -80,8 +153,7 @@ void ShenandoahGlobalHeuristics::choose_global_collection_set(ShenandoahCollecti if (young_evac_reserve > unaffiliated_young_memory) { shared_reserve_regions += unaffiliated_young_regions; } else { - size_t delta_regions = young_evac_reserve / region_size_bytes; - shared_reserve_regions += delta_regions; + shared_reserve_regions += young_evac_reserve / region_size_bytes; } young_evac_reserve = 0; size_t total_old_reserve = old_evac_reserve + old_promo_reserve; @@ -90,24 +162,15 @@ void ShenandoahGlobalHeuristics::choose_global_collection_set(ShenandoahCollecti shared_reserve_regions += unaffiliated_old_regions; old_promo_reserve = total_old_reserve - unaffiliated_old_memory; } else { - size_t delta_regions = old_evac_reserve / region_size_bytes; - shared_reserve_regions += delta_regions; + shared_reserve_regions += old_evac_reserve / region_size_bytes; } old_evac_reserve = 0; assert(shared_reserve_regions <= (heap->young_generation()->free_unaffiliated_regions() + heap->old_generation()->free_unaffiliated_regions()), - "simple math"); - - size_t shared_reserves = shared_reserve_regions * region_size_bytes; - size_t committed_from_shared_reserves = 0; - - size_t promo_bytes = 0; - size_t old_evac_bytes = 0; - size_t young_evac_bytes = 0; - - size_t consumed_by_promo = 0; // promo_bytes * ShenandoahPromoEvacWaste - size_t consumed_by_old_evac = 0; // old_evac_bytes * ShenandoahOldEvacWaste - size_t consumed_by_young_evac = 0; // young_evac_bytes * ShenandoahEvacWaste + "Shared reserve regions (%zu) should not exceed total unaffiliated regions (young: %zu, old: %zu)", + shared_reserve_regions, + heap->young_generation()->free_unaffiliated_regions(), + heap->old_generation()->free_unaffiliated_regions()); // Of the memory reclaimed by GC, some of this will need to be reserved for the next GC collection. Use the current // young reserve as an approximation of the future Collector reserve requirement. Try to end with at least @@ -115,147 +178,93 @@ void ShenandoahGlobalHeuristics::choose_global_collection_set(ShenandoahCollecti size_t free_target = (capacity * ShenandoahMinFreeThreshold) / 100 + original_young_evac_reserve; size_t min_garbage = (free_target > actual_free) ? (free_target - actual_free) : 0; - size_t aged_regions_promoted = 0; - size_t young_regions_evacuated = 0; - size_t old_regions_evacuated = 0; + ShenandoahGlobalCSetBudget budget(region_size_bytes, + shared_reserve_regions * region_size_bytes, + garbage_threshold, ignore_threshold, min_garbage, + young_evac_reserve, ShenandoahEvacWaste, + old_evac_reserve, ShenandoahOldEvacWaste, + old_promo_reserve, ShenandoahPromoEvacWaste); + budget.set_cur_garbage(cur_young_garbage); - log_info(gc, ergo)("Adaptive CSet Selection for GLOBAL. Discretionary evacuation budget (for either old or young): %zu%s" - ", Actual Free: %zu%s.", - byte_size_in_proper_unit(shared_reserves), proper_unit_for_byte_size(shared_reserves), - byte_size_in_proper_unit(actual_free), proper_unit_for_byte_size(actual_free)); + log_info(gc, ergo)("Adaptive CSet Selection for global cycle. Discretionary evacuation budget (for either old or young): " PROPERFMT ", Actual Free: " PROPERFMT, + PROPERFMTARGS(budget.shared_reserves()), PROPERFMTARGS(actual_free)); - size_t cur_garbage = cur_young_garbage; for (size_t idx = 0; idx < size; idx++) { ShenandoahHeapRegion* r = data[idx].get_region(); - assert(!cset->is_preselected(r->index()), "There should be no preselected regions during GLOBAL GC"); - bool add_region = false; - size_t region_garbage = r->garbage(); - size_t new_garbage = cur_garbage + region_garbage; - bool add_regardless = (region_garbage > ignore_threshold) && (new_garbage < min_garbage); - size_t live_bytes = r->get_live_data_bytes(); - if (add_regardless || (region_garbage >= garbage_threshold)) { - if (r->is_old()) { - size_t anticipated_consumption = (size_t) (live_bytes * ShenandoahOldEvacWaste); - size_t new_old_consumption = consumed_by_old_evac + anticipated_consumption; - size_t new_old_evac_reserve = old_evac_reserve; - size_t proposed_old_region_expansion = 0; - while ((new_old_consumption > new_old_evac_reserve) && (committed_from_shared_reserves < shared_reserves)) { - committed_from_shared_reserves += region_size_bytes; - proposed_old_region_expansion++; - new_old_evac_reserve += region_size_bytes; - } - // If this region has free memory and we choose to place it in the collection set, its free memory is no longer - // available to hold promotion results. So we behave as if its free memory is consumed within the promotion reserve. - size_t anticipated_loss_from_promo_reserve = r->free(); - size_t new_promo_consumption = consumed_by_promo + anticipated_loss_from_promo_reserve; - size_t new_promo_reserve = old_promo_reserve; - while ((new_promo_consumption > new_promo_reserve) && (committed_from_shared_reserves < shared_reserves)) { - committed_from_shared_reserves += region_size_bytes; - proposed_old_region_expansion++; - new_promo_reserve += region_size_bytes; - } - if ((new_old_consumption <= new_old_evac_reserve) && (new_promo_consumption <= new_promo_reserve)) { - add_region = true; - old_evac_reserve = new_old_evac_reserve; - old_promo_reserve = new_promo_reserve; - old_evac_bytes += live_bytes; - consumed_by_old_evac = new_old_consumption; - consumed_by_promo = new_promo_consumption; - cur_garbage = new_garbage; - old_regions_evacuated++; - } else { - // We failed to sufficiently expand old so unwind proposed expansion - committed_from_shared_reserves -= proposed_old_region_expansion * region_size_bytes; - } - } else if (heap->is_tenurable(r)) { - size_t anticipated_consumption = (size_t) (live_bytes * ShenandoahPromoEvacWaste); - size_t new_promo_consumption = consumed_by_promo + anticipated_consumption; - size_t new_promo_reserve = old_promo_reserve; - size_t proposed_old_region_expansion = 0; - while ((new_promo_consumption > new_promo_reserve) && (committed_from_shared_reserves < shared_reserves)) { - committed_from_shared_reserves += region_size_bytes; - proposed_old_region_expansion++; - new_promo_reserve += region_size_bytes; - } - if (new_promo_consumption <= new_promo_reserve) { - add_region = true; - old_promo_reserve = new_promo_reserve; - promo_bytes += live_bytes; - consumed_by_promo = new_promo_consumption; - cur_garbage = new_garbage; - aged_regions_promoted++; - } else { - // We failed to sufficiently expand old so unwind proposed expansion - committed_from_shared_reserves -= proposed_old_region_expansion * region_size_bytes; - } - } else { - assert(r->is_young() && !heap->is_tenurable(r), "DeMorgan's law (assuming r->is_affiliated)"); - size_t anticipated_consumption = (size_t) (live_bytes * ShenandoahEvacWaste); - size_t new_young_evac_consumption = consumed_by_young_evac + anticipated_consumption; - size_t new_young_evac_reserve = young_evac_reserve; - size_t proposed_young_region_expansion = 0; - while ((new_young_evac_consumption > new_young_evac_reserve) && (committed_from_shared_reserves < shared_reserves)) { - committed_from_shared_reserves += region_size_bytes; - proposed_young_region_expansion++; - new_young_evac_reserve += region_size_bytes; - } - if (new_young_evac_consumption <= new_young_evac_reserve) { - add_region = true; - young_evac_reserve = new_young_evac_reserve; - young_evac_bytes += live_bytes; - consumed_by_young_evac = new_young_evac_consumption; - cur_garbage = new_garbage; - young_regions_evacuated++; - } else { - // We failed to sufficiently expand old so unwind proposed expansion - committed_from_shared_reserves -= proposed_young_region_expansion * region_size_bytes; - } - } + if (cset->is_in(r) || r->get_top_before_promote() != nullptr) { + assert(heap->is_tenurable(r), "Region %zu already selected for promotion must be tenurable", idx); + continue; } - if (add_region) { + + ShenandoahGlobalRegionAttributes attrs; + attrs.garbage = r->garbage(); + attrs.live_data_bytes = r->get_live_data_bytes(); + attrs.free_bytes = r->free(); + attrs.is_old = r->is_old(); + attrs.is_tenurable = !r->is_old() && heap->is_tenurable(r); + + if (budget.try_add_region(attrs) != ShenandoahGlobalRegionDisposition::SKIP) { cset->add_region(r); } } - if (committed_from_shared_reserves < shared_reserves) { - // Give all the rest to promotion - old_promo_reserve += (shared_reserves - committed_from_shared_reserves); - // dead code: committed_from_shared_reserves = shared_reserves; - } + budget.finish(); - // Consider the effects of round-off: - // 1. We know that the sum over each evacuation mutiplied by Evacuation Waste is <= total evacuation reserve - // 2. However, the reserve for each individual evacuation may be rounded down. In the worst case, we will be over budget - // by the number of regions evacuated, since each region's reserve might be under-estimated by at most 1 - // 3. Likewise, if we take the sum of bytes evacuated and multiply this by the Evacuation Waste and then round down - // to nearest integer, the calculated reserve will underestimate the true reserve needs by at most 1. - // 4. This explains the adjustments to subtotals in the assert statements below. - assert(young_evac_bytes * ShenandoahEvacWaste <= young_evac_reserve + young_regions_evacuated, - "budget: %zu <= %zu", (size_t) (young_evac_bytes * ShenandoahEvacWaste), young_evac_reserve); - assert(old_evac_bytes * ShenandoahOldEvacWaste <= old_evac_reserve + old_regions_evacuated, - "budget: %zu <= %zu", (size_t) (old_evac_bytes * ShenandoahOldEvacWaste), old_evac_reserve); - assert(promo_bytes * ShenandoahPromoEvacWaste <= old_promo_reserve + aged_regions_promoted, - "budget: %zu <= %zu", (size_t) (promo_bytes * ShenandoahPromoEvacWaste), old_promo_reserve); - assert(young_evac_reserve + old_evac_reserve + old_promo_reserve <= - heap->young_generation()->get_evacuation_reserve() + heap->old_generation()->get_evacuation_reserve() + - heap->old_generation()->get_promoted_reserve(), "Exceeded budget"); + DEBUG_ONLY(budget.assert_budget_constraints_hold( + heap->young_generation()->get_evacuation_reserve() + + heap->old_generation()->get_evacuation_reserve() + + heap->old_generation()->get_promoted_reserve())); - if (heap->young_generation()->get_evacuation_reserve() < young_evac_reserve) { - size_t delta_bytes = young_evac_reserve - heap->young_generation()->get_evacuation_reserve(); + if (heap->young_generation()->get_evacuation_reserve() < budget.young_evac.reserve()) { + size_t delta_bytes = budget.young_evac.reserve() - heap->young_generation()->get_evacuation_reserve(); size_t delta_regions = delta_bytes / region_size_bytes; size_t regions_to_transfer = MIN2(unaffiliated_old_regions, delta_regions); log_info(gc)("Global GC moves %zu unaffiliated regions from old collector to young collector reserves", regions_to_transfer); ssize_t negated_regions = -regions_to_transfer; heap->free_set()->move_unaffiliated_regions_from_collector_to_old_collector(negated_regions); - } else if (heap->young_generation()->get_evacuation_reserve() > young_evac_reserve) { - size_t delta_bytes = heap->young_generation()->get_evacuation_reserve() - young_evac_reserve; + } else if (heap->young_generation()->get_evacuation_reserve() > budget.young_evac.reserve()) { + size_t delta_bytes = heap->young_generation()->get_evacuation_reserve() - budget.young_evac.reserve(); size_t delta_regions = delta_bytes / region_size_bytes; size_t regions_to_transfer = MIN2(unaffiliated_young_regions, delta_regions); log_info(gc)("Global GC moves %zu unaffiliated regions from young collector to old collector reserves", regions_to_transfer); heap->free_set()->move_unaffiliated_regions_from_collector_to_old_collector(regions_to_transfer); } - heap->young_generation()->set_evacuation_reserve(young_evac_reserve); - heap->old_generation()->set_evacuation_reserve(old_evac_reserve); - heap->old_generation()->set_promoted_reserve(old_promo_reserve); + heap->young_generation()->set_evacuation_reserve(budget.young_evac.reserve()); + heap->old_generation()->set_evacuation_reserve(budget.old_evac.reserve()); + heap->old_generation()->set_promoted_reserve(budget.promo.reserve()); } + +#ifdef ASSERT +void ShenandoahGlobalCSetBudget::assert_budget_constraints_hold(size_t original_total_reserves) const { + // Consider the effects of round-off: + // 1. We know that the sum over each evacuation multiplied by Evacuation Waste is <= total evacuation reserve + // 2. However, the reserve for each individual evacuation may be rounded down. In the worst case, we will be + // over budget by the number of regions evacuated, since each region's reserve might be under-estimated by + // at most 1. + // 3. Likewise, if we take the sum of bytes evacuated and multiply this by the Evacuation Waste and then round + // down to nearest integer, the calculated reserve will underestimate the true reserve needs by at most 1. + // 4. This explains the adjustments to subtotals in the assert statements below. + assert(young_evac.live_bytes() * young_evac.waste_factor() <= + young_evac.reserve() + young_evac.region_count(), + "Young evac consumption (%zu) exceeds reserve (%zu) + region count (%zu)", + (size_t)(young_evac.live_bytes() * young_evac.waste_factor()), + young_evac.reserve(), young_evac.region_count()); + assert(old_evac.live_bytes() * old_evac.waste_factor() <= + old_evac.reserve() + old_evac.region_count(), + "Old evac consumption (%zu) exceeds reserve (%zu) + region count (%zu)", + (size_t)(old_evac.live_bytes() * old_evac.waste_factor()), + old_evac.reserve(), old_evac.region_count()); + assert(promo.live_bytes() * promo.waste_factor() <= + promo.reserve() + promo.region_count(), + "Promo consumption (%zu) exceeds reserve (%zu) + region count (%zu)", + (size_t)(promo.live_bytes() * promo.waste_factor()), + promo.reserve(), promo.region_count()); + + size_t total_post_reserves = young_evac.reserve() + old_evac.reserve() + promo.reserve(); + assert(total_post_reserves <= original_total_reserves, + "Total post-cset reserves (%zu + %zu + %zu = %zu) exceed original reserves (%zu)", + young_evac.reserve(), old_evac.reserve(), promo.reserve(), + total_post_reserves, original_total_reserves); +} +#endif diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp index 1f95f75c521..8102fa24d14 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp @@ -25,16 +25,138 @@ #ifndef SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHGLOBALHEURISTICS_HPP #define SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHGLOBALHEURISTICS_HPP - #include "gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp" class ShenandoahGlobalGeneration; -/* - * This is a specialization of the generational heuristics which is aware - * of old and young regions and respects the configured evacuation parameters - * for such regions during a global collection of a generational heap. - */ +enum class ShenandoahGlobalRegionDisposition { + SKIP, + ADD_OLD_EVAC, + ADD_PROMO, + ADD_YOUNG_EVAC +}; + +// A shared pool of evacuation reserves that can be drawn from by any +// evacuation category. Owned by ShenandoahGlobalCSetBudget; each +// ShenandoahEvacuationBudget holds a pointer to it. +struct ShenandoahSharedEvacReserve { + size_t limit; + size_t committed; + + ShenandoahSharedEvacReserve(size_t limit) : limit(limit), committed(0) {} +}; + +// Tracks the budget for a single evacuation category. +class ShenandoahEvacuationBudget { + size_t _reserve; + size_t _consumed; + size_t _live_bytes; + size_t _region_count; + size_t _region_size_bytes; + double _waste_factor; + ShenandoahSharedEvacReserve* _shared; + +public: + ShenandoahEvacuationBudget(size_t reserve, double waste_factor, + size_t region_size_bytes, + ShenandoahSharedEvacReserve* shared) + : _reserve(reserve), _consumed(0), _live_bytes(0), + _region_count(0), _region_size_bytes(region_size_bytes), + _waste_factor(waste_factor), _shared(shared) {} + + size_t anticipated_consumption(size_t live_bytes) const { + return (size_t)(live_bytes * _waste_factor); + } + + // Try to reserve 'bytes' from this budget, expanding from the shared + // pool if necessary. On success, updates _reserve and shared->committed + // and returns true. On failure, nothing is modified. + bool try_reserve(size_t bytes); + + // Record that a region was accepted. + void commit(size_t consumption, size_t live_bytes); + + // Record a raw consumption (e.g. free bytes lost from promo reserve). + void commit_raw(size_t bytes) { _consumed += bytes; } + + size_t reserve() const { return _reserve; } + size_t consumed() const { return _consumed; } + size_t live_bytes() const { return _live_bytes; } + size_t region_count() const { return _region_count; } + double waste_factor() const { return _waste_factor; } + + void add_to_reserve(size_t bytes) { _reserve += bytes; } + void set_reserve(size_t bytes) { _reserve = bytes; } +}; + +// These are the attributes of a region required to decide if it can be +// added to the collection set or not. +struct ShenandoahGlobalRegionAttributes { + size_t garbage; + size_t live_data_bytes; + size_t free_bytes; + bool is_old; + bool is_tenurable; +}; + +// This class consolidates all of the data required to build a global +// collection set. Critically, it takes no dependencies on any classes +// that themselves depend on ShenandoahHeap. This makes it possible to +// write extensive unit tests for this complex code. +class ShenandoahGlobalCSetBudget { + size_t _region_size_bytes; + size_t _garbage_threshold; + size_t _ignore_threshold; + size_t _min_garbage; + size_t _cur_garbage; + + ShenandoahSharedEvacReserve _shared; + +public: + ShenandoahEvacuationBudget young_evac; + ShenandoahEvacuationBudget old_evac; + ShenandoahEvacuationBudget promo; + + ShenandoahGlobalCSetBudget(size_t region_size_bytes, + size_t shared_reserves, + size_t garbage_threshold, + size_t ignore_threshold, + size_t min_garbage, + size_t young_evac_reserve, double young_waste, + size_t old_evac_reserve, double old_waste, + size_t promo_reserve, double promo_waste) + : _region_size_bytes(region_size_bytes), + _garbage_threshold(garbage_threshold), + _ignore_threshold(ignore_threshold), + _min_garbage(min_garbage), + _cur_garbage(0), + _shared(shared_reserves), + young_evac(young_evac_reserve, young_waste, region_size_bytes, &_shared), + old_evac(old_evac_reserve, old_waste, region_size_bytes, &_shared), + promo(promo_reserve, promo_waste, region_size_bytes, &_shared) {} + + ShenandoahGlobalRegionDisposition try_add_region(const ShenandoahGlobalRegionAttributes& region); + + // Any remaining shared budget is given to the promotion reserve. + void finish() { + if (_shared.committed < _shared.limit) { + promo.add_to_reserve(_shared.limit - _shared.committed); + } + } + + // Verify that the budget invariants hold after collection set selection. + // original_total_reserves is the sum of the young, old, and promo evacuation + // reserves as they were before the budget was constructed. + DEBUG_ONLY(void assert_budget_constraints_hold(size_t original_total_reserves) const;) + + size_t region_size_bytes() const { return _region_size_bytes; } + size_t shared_reserves() const { return _shared.limit; } + size_t committed_from_shared() const { return _shared.committed; } + size_t cur_garbage() const { return _cur_garbage; } + + void set_cur_garbage(size_t g) { _cur_garbage = g; } +}; + class ShenandoahGlobalHeuristics : public ShenandoahGenerationalHeuristics { public: ShenandoahGlobalHeuristics(ShenandoahGlobalGeneration* generation); @@ -50,5 +172,4 @@ private: size_t cur_young_garbage) const; }; - #endif // SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHGLOBALHEURISTICS_HPP diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp index 603e00c401d..895088381ee 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp @@ -122,13 +122,7 @@ void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collec } } else if (region->is_humongous_start()) { // Reclaim humongous regions here, and count them as the immediate garbage -#ifdef ASSERT - bool reg_live = region->has_live(); - bool bm_live = heap->global_generation()->complete_marking_context()->is_marked(cast_to_oop(region->bottom())); - assert(reg_live == bm_live, - "Humongous liveness and marks should agree. Region live: %s; Bitmap live: %s; Region Live Words: %zu", - BOOL_TO_STR(reg_live), BOOL_TO_STR(bm_live), region->get_live_data_words()); -#endif + DEBUG_ONLY(assert_humongous_mark_consistency(region)); if (!region->has_live()) { heap->trash_humongous_region_at(region); @@ -137,7 +131,7 @@ void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collec immediate_garbage += garbage; } } else if (region->is_trash()) { - // Count in just trashed collection set, during coalesced CM-with-UR + // Count in just trashed humongous continuation regions immediate_regions++; immediate_garbage += garbage; } @@ -145,13 +139,11 @@ void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collec // Step 2. Look back at garbage statistics, and decide if we want to collect anything, // given the amount of immediately reclaimable garbage. If we do, figure out the collection set. + assert(immediate_garbage <= total_garbage, + "Cannot have more immediate garbage than total garbage: " PROPERFMT " vs " PROPERFMT, + PROPERFMTARGS(immediate_garbage), PROPERFMTARGS(total_garbage)); - assert (immediate_garbage <= total_garbage, - "Cannot have more immediate garbage than total garbage: %zu%s vs %zu%s", - byte_size_in_proper_unit(immediate_garbage), proper_unit_for_byte_size(immediate_garbage), - byte_size_in_proper_unit(total_garbage), proper_unit_for_byte_size(total_garbage)); - - size_t immediate_percent = (total_garbage == 0) ? 0 : (immediate_garbage * 100 / total_garbage); + const size_t immediate_percent = (total_garbage == 0) ? 0 : (immediate_garbage * 100 / total_garbage); if (immediate_percent <= ShenandoahImmediateThreshold) { choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free); @@ -303,3 +295,16 @@ double ShenandoahHeuristics::elapsed_degenerated_cycle_time() const { double now = os::elapsedTime(); return now - _precursor_cycle_start; } + +#ifdef ASSERT +void ShenandoahHeuristics::assert_humongous_mark_consistency(ShenandoahHeapRegion* region) { + assert(region->is_humongous(), "Region %zu must be humongous", region->index()); + const oop humongous_oop = cast_to_oop(region->bottom()); + ShenandoahGeneration* generation = ShenandoahHeap::heap()->generation_for(region->affiliation()); + const bool bm_live = generation->complete_marking_context()->is_marked(humongous_oop); + const bool reg_live = region->has_live(); + assert(reg_live == bm_live, + "Humongous liveness and marks should agree. Region live: %s; Bitmap live: %s; Region Live Words: %zu", + BOOL_TO_STR(reg_live), BOOL_TO_STR(bm_live), region->get_live_data_words()); +} +#endif diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp index 5bfba4f52d5..9066cdfccac 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp @@ -285,6 +285,8 @@ public: // Format prefix and emit log message indicating a GC cycle hs been triggered void log_trigger(const char* fmt, ...) ATTRIBUTE_PRINTF(2, 3); + + DEBUG_ONLY(static void assert_humongous_mark_consistency(ShenandoahHeapRegion* region)); }; #endif // SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHHEURISTICS_HPP diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp index e0cab781674..4f71562f4f6 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp @@ -79,7 +79,6 @@ ShenandoahOldHeuristics::ShenandoahOldHeuristics(ShenandoahOldGeneration* genera } bool ShenandoahOldHeuristics::prime_collection_set(ShenandoahCollectionSet* collection_set) { - _mixed_evac_cset = collection_set; _included_old_regions = 0; _evacuated_old_bytes = 0; _collected_old_bytes = 0; @@ -106,10 +105,6 @@ bool ShenandoahOldHeuristics::prime_collection_set(ShenandoahCollectionSet* coll _first_pinned_candidate = NOT_FOUND; - uint included_old_regions = 0; - size_t evacuated_old_bytes = 0; - size_t collected_old_bytes = 0; - // If a region is put into the collection set, then this region's free (not yet used) bytes are no longer // "available" to hold the results of other evacuations. This may cause a decrease in the remaining amount // of memory that can still be evacuated. We address this by reducing the evacuation budget by the amount @@ -152,7 +147,7 @@ bool ShenandoahOldHeuristics::prime_collection_set(ShenandoahCollectionSet* coll log_debug(gc)("Choose old regions for mixed collection: old evacuation budget: " PROPERFMT ", candidates: %u", PROPERFMTARGS(_old_evacuation_budget), unprocessed_old_collection_candidates()); - return add_old_regions_to_cset(); + return add_old_regions_to_cset(collection_set); } bool ShenandoahOldHeuristics::all_candidates_are_pinned() { @@ -226,7 +221,7 @@ void ShenandoahOldHeuristics::slide_pinned_regions_to_front() { _next_old_collection_candidate = write_index + 1; } -bool ShenandoahOldHeuristics::add_old_regions_to_cset() { +bool ShenandoahOldHeuristics::add_old_regions_to_cset(ShenandoahCollectionSet* collection_set) { if (unprocessed_old_collection_candidates() == 0) { return false; } @@ -310,7 +305,7 @@ bool ShenandoahOldHeuristics::add_old_regions_to_cset() { break; } } - _mixed_evac_cset->add_region(r); + collection_set->add_region(r); _included_old_regions++; _evacuated_old_bytes += live_data_for_evacuation; _collected_old_bytes += r->garbage(); @@ -356,7 +351,7 @@ bool ShenandoahOldHeuristics::finalize_mixed_evacs() { return (_included_old_regions > 0); } -bool ShenandoahOldHeuristics::top_off_collection_set(size_t &add_regions_to_old) { +bool ShenandoahOldHeuristics::top_off_collection_set(ShenandoahCollectionSet* collection_set, size_t &add_regions_to_old) { if (unprocessed_old_collection_candidates() == 0) { add_regions_to_old = 0; return false; @@ -367,11 +362,10 @@ bool ShenandoahOldHeuristics::top_off_collection_set(size_t &add_regions_to_old) // We have budgeted to assure the live_bytes_in_tenurable_regions() get evacuated into old generation. Young reserves // only for untenurable region evacuations. - size_t planned_young_evac = _mixed_evac_cset->get_live_bytes_in_untenurable_regions(); + size_t planned_young_evac = collection_set->get_live_bytes_in_untenurable_regions(); size_t consumed_from_young_cset = (size_t) (planned_young_evac * ShenandoahEvacWaste); size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); - size_t regions_required_for_collector_reserve = (consumed_from_young_cset + region_size_bytes - 1) / region_size_bytes; assert(consumed_from_young_cset <= max_young_cset, "sanity"); assert(max_young_cset <= young_unaffiliated_regions * region_size_bytes, "sanity"); @@ -399,7 +393,7 @@ bool ShenandoahOldHeuristics::top_off_collection_set(size_t &add_regions_to_old) _old_generation->augment_evacuation_reserve(budget_supplement); young_generation->set_evacuation_reserve(max_young_cset - budget_supplement); - return add_old_regions_to_cset(); + return add_old_regions_to_cset(collection_set); } else { add_regions_to_old = 0; return false; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp index e657ac58ae4..04a92d28248 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp @@ -106,7 +106,6 @@ private: // when client code invokes prime_collection_set(). They are consulted, and sometimes modified, when client code // calls top_off_collection_set() to possibly expand the number of old-gen regions in a mixed evacuation cset, and by // finalize_mixed_evacs(), which prepares the way for mixed evacuations to begin. - ShenandoahCollectionSet* _mixed_evac_cset; size_t _evacuated_old_bytes; size_t _collected_old_bytes; size_t _included_old_regions; @@ -163,7 +162,7 @@ private: // a conservative old evacuation budget, and the second time with a larger more aggressive old evacuation budget. Returns // true iff we need to finalize mixed evacs. (If no regions are added to the collection set, there is no need to finalize // mixed evacuations.) - bool add_old_regions_to_cset(); + bool add_old_regions_to_cset(ShenandoahCollectionSet* collection_set); public: explicit ShenandoahOldHeuristics(ShenandoahOldGeneration* generation, ShenandoahGenerationalHeap* gen_heap); @@ -180,7 +179,7 @@ public: // evacuation candidate regions into the collection set as will fit within this excess repurposed reserved. // Returns true iff we need to finalize mixed evacs. Upon return, the var parameter regions_to_xfer holds the // number of regions to transfer from young to old. - bool top_off_collection_set(size_t &add_regions_to_old); + bool top_off_collection_set(ShenandoahCollectionSet* collection_set, size_t &add_regions_to_old); // Having added all eligible mixed-evacuation candidates to the collection set, this function updates the total count // of how much old-gen memory remains to be evacuated and adjusts the representation of old-gen regions that remain to diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp index 09508a1163f..68ffb6592db 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp @@ -54,15 +54,13 @@ void ShenandoahYoungHeuristics::choose_collection_set_from_regiondata(Shenandoah // Better select garbage-first regions QuickSort::sort(data, size, compare_by_garbage); - size_t cur_young_garbage = add_preselected_regions_to_collection_set(cset, data, size); - - choose_young_collection_set(cset, data, size, actual_free, cur_young_garbage); + choose_young_collection_set(cset, data, size, actual_free); // Especially when young-gen trigger is expedited in order to finish mixed evacuations, there may not be // enough consolidated garbage to make effective use of young-gen evacuation reserve. If there is still // young-gen reserve available following selection of the young-gen collection set, see if we can use // this memory to expand the old-gen evacuation collection set. - need_to_finalize_mixed |= heap->old_generation()->heuristics()->top_off_collection_set(_add_regions_to_old); + need_to_finalize_mixed |= heap->old_generation()->heuristics()->top_off_collection_set(cset, _add_regions_to_old); if (need_to_finalize_mixed) { heap->old_generation()->heuristics()->finalize_mixed_evacs(); } @@ -70,8 +68,7 @@ void ShenandoahYoungHeuristics::choose_collection_set_from_regiondata(Shenandoah void ShenandoahYoungHeuristics::choose_young_collection_set(ShenandoahCollectionSet* cset, const RegionData* data, - size_t size, size_t actual_free, - size_t cur_young_garbage) const { + size_t size, size_t actual_free) const { const auto heap = ShenandoahGenerationalHeap::heap(); @@ -82,23 +79,23 @@ void ShenandoahYoungHeuristics::choose_young_collection_set(ShenandoahCollection // This is young-gen collection or a mixed evacuation. // If this is mixed evacuation, the old-gen candidate regions have already been added. size_t cur_cset = 0; + size_t cur_young_garbage = cset->garbage(); const size_t max_cset = (size_t) (heap->young_generation()->get_evacuation_reserve() / ShenandoahEvacWaste); const size_t free_target = (capacity * ShenandoahMinFreeThreshold) / 100 + max_cset; const size_t min_garbage = (free_target > actual_free) ? (free_target - actual_free) : 0; - log_info(gc, ergo)( - "Adaptive CSet Selection for YOUNG. Max Evacuation: %zu%s, Actual Free: %zu%s.", - byte_size_in_proper_unit(max_cset), proper_unit_for_byte_size(max_cset), - byte_size_in_proper_unit(actual_free), proper_unit_for_byte_size(actual_free)); + "Adaptive CSet Selection for YOUNG. Max Evacuation: " PROPERFMT ", Actual Free: " PROPERFMT, + PROPERFMTARGS(max_cset), PROPERFMTARGS(actual_free)); for (size_t idx = 0; idx < size; idx++) { ShenandoahHeapRegion* r = data[idx].get_region(); - if (cset->is_preselected(r->index())) { + if (cset->is_in(r) || r->get_top_before_promote() != nullptr) { + assert(heap->is_tenurable(r), "Region %zu already selected for promotion must be tenurable", idx); continue; } - // Note that we do not add tenurable regions if they were not pre-selected. They were not preselected + // Note that we do not add tenurable regions if they were not pre-selected. They were not selected // because there is insufficient room in old-gen to hold their to-be-promoted live objects or because // they are to be promoted in place. if (!heap->is_tenurable(r)) { @@ -180,10 +177,8 @@ size_t ShenandoahYoungHeuristics::bytes_of_allocation_runway_before_gc_trigger(s size_t usage = _space_info->used(); size_t available = (capacity > usage)? capacity - usage: 0; size_t allocated = _free_set->get_bytes_allocated_since_gc_start(); + size_t anticipated_available = available + young_regions_to_be_reclaimed * ShenandoahHeapRegion::region_size_bytes(); - size_t available_young_collected = ShenandoahHeap::heap()->collection_set()->get_young_available_bytes_collected(); - size_t anticipated_available = - available + young_regions_to_be_reclaimed * ShenandoahHeapRegion::region_size_bytes() - available_young_collected; size_t spike_headroom = capacity * ShenandoahAllocSpikeFactor / 100; size_t penalties = capacity * _gc_time_penalties / 100; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp index b9d64059680..806cef673d5 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp @@ -49,8 +49,7 @@ public: private: void choose_young_collection_set(ShenandoahCollectionSet* cset, const RegionData* data, - size_t size, size_t actual_free, - size_t cur_young_garbage) const; + size_t size, size_t actual_free) const; }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp index 004558a9fa8..cb6ff795c07 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp @@ -166,10 +166,9 @@ void ShenandoahBarrierSet::on_thread_detach(Thread *thread) { gclab->retire(); } - PLAB* plab = ShenandoahThreadLocalData::plab(thread); - if (plab != nullptr) { - // This will assert if plab is not null in non-generational mode - ShenandoahGenerationalHeap::heap()->retire_plab(plab); + ShenandoahPLAB* shenandoah_plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + if (shenandoah_plab != nullptr) { + shenandoah_plab->retire(); } // SATB protocol requires to keep alive reachable oops from roots at the beginning of GC diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp index 54ab7801ca9..d2a489a15be 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp @@ -49,7 +49,6 @@ ShenandoahCollectionSet::ShenandoahCollectionSet(ShenandoahHeap* heap, ReservedS _live(0), _region_count(0), _old_garbage(0), - _preselected_regions(nullptr), _young_available_bytes_collected(0), _old_available_bytes_collected(0), _current_index(0) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.hpp index 87b4597c93f..7722423709d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.hpp @@ -36,13 +36,6 @@ class ShenandoahCollectionSet : public CHeapObj { friend class ShenandoahHeap; - friend class ShenandoahCollectionSetPreselector; - - void establish_preselected(bool *preselected) { - assert(_preselected_regions == nullptr, "Over-writing"); - _preselected_regions = preselected; - } - void abandon_preselected() { _preselected_regions = nullptr; } private: size_t const _map_size; @@ -67,11 +60,6 @@ private: // How many bytes of old garbage are present in a mixed collection set? size_t _old_garbage; - // Points to array identifying which tenure-age regions have been preselected - // for inclusion in collection set. This field is only valid during brief - // spans of time while collection set is being constructed. - bool* _preselected_regions; - // When a region having memory available to be allocated is added to the collection set, the region's available memory // should be subtracted from what's available. size_t _young_available_bytes_collected; @@ -132,16 +120,6 @@ public: // Returns the amount of garbage in old regions in the collection set. inline size_t get_old_garbage() const; - bool is_preselected(size_t region_idx) { - assert(_preselected_regions != nullptr, "Missing establish after abandon"); - return _preselected_regions[region_idx]; - } - - bool* preselected_regions() { - assert(_preselected_regions != nullptr, "Null ptr"); - return _preselected_regions; - } - bool has_old_regions() const { return _has_old_regions; } size_t used() const { return _used; } size_t live() const { return _live; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index c5607421265..48183507124 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -45,7 +45,7 @@ ShenandoahControlThread::ShenandoahControlThread() : _requested_gc_cause(GCCause::_no_gc), _degen_point(ShenandoahGC::_degenerated_outside_cycle), _control_lock(CONTROL_LOCK_RANK, "ShenandoahControl_lock", true) { - set_name("Shenandoah Control Thread"); + set_name("ShenControl"); create_and_start(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index c39e2e7bb79..a579d6d3694 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -348,7 +348,7 @@ void ShenandoahRegionPartitions::make_all_regions_unavailable() { _leftmosts[partition_id] = _max; _rightmosts[partition_id] = -1; _leftmosts_empty[partition_id] = _max; - _rightmosts_empty[partition_id] = -1;; + _rightmosts_empty[partition_id] = -1; _capacity[partition_id] = 0; _region_counts[partition_id] = 0; _empty_region_counts[partition_id] = 0; @@ -445,7 +445,7 @@ void ShenandoahRegionPartitions::increase_humongous_waste(ShenandoahFreeSetParti size_t ShenandoahRegionPartitions::get_humongous_waste(ShenandoahFreeSetPartitionId which_partition) { assert (which_partition < NumPartitions, "Partition must be valid"); - return _humongous_waste[int(which_partition)];; + return _humongous_waste[int(which_partition)]; } void ShenandoahRegionPartitions::set_capacity_of(ShenandoahFreeSetPartitionId which_partition, size_t value) { @@ -510,7 +510,7 @@ void ShenandoahRegionPartitions::decrease_available(ShenandoahFreeSetPartitionId size_t ShenandoahRegionPartitions::get_available(ShenandoahFreeSetPartitionId which_partition) { assert (which_partition < NumPartitions, "Partition must be valid"); - return _available[int(which_partition)];; + return _available[int(which_partition)]; } void ShenandoahRegionPartitions::increase_region_counts(ShenandoahFreeSetPartitionId which_partition, size_t regions) { @@ -2799,7 +2799,7 @@ size_t ShenandoahFreeSet::reserve_regions(size_t to_reserve, size_t to_reserve_o size_t empty_regions_to_collector = 0; size_t empty_regions_to_old_collector = 0; - size_t old_collector_available = _partitions.available_in(ShenandoahFreeSetPartitionId::OldCollector);; + size_t old_collector_available = _partitions.available_in(ShenandoahFreeSetPartitionId::OldCollector); size_t collector_available = _partitions.available_in(ShenandoahFreeSetPartitionId::Collector); for (size_t i = _heap->num_regions(); i > 0; i--) { @@ -2848,7 +2848,7 @@ size_t ShenandoahFreeSet::reserve_regions(size_t to_reserve, size_t to_reserve_o _partitions.leftmost(ShenandoahFreeSetPartitionId::OldCollector), _partitions.rightmost(ShenandoahFreeSetPartitionId::OldCollector)); old_region_count++; - assert(ac = ShenandoahHeapRegion::region_size_bytes(), "Cannot move to old unless entire region is in alloc capacity"); + assert(ac == ShenandoahHeapRegion::region_size_bytes(), "Cannot move to old unless entire region is in alloc capacity"); mutator_allocatable_words -= ShenandoahHeapRegion::region_size_words(); continue; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp index b2d5e5423dd..750389ae6c3 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp @@ -315,8 +315,8 @@ void ShenandoahGeneration::prepare_regions_and_collection_set(bool concurrent) { _free_set->prepare_to_rebuild(young_trashed_regions, old_trashed_regions, first_old, last_old, num_old); if (heap->mode()->is_generational()) { ShenandoahGenerationalHeap* gen_heap = ShenandoahGenerationalHeap::heap(); - size_t allocation_runway = - gen_heap->young_generation()->heuristics()->bytes_of_allocation_runway_before_gc_trigger(young_trashed_regions); + size_t allocation_runway = + gen_heap->young_generation()->heuristics()->bytes_of_allocation_runway_before_gc_trigger(young_trashed_regions); gen_heap->compute_old_generation_balance(allocation_runway, old_trashed_regions, young_trashed_regions); } _free_set->finish_rebuild(young_trashed_regions, old_trashed_regions, num_old); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index cc7547b8ac1..1edff443ded 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -55,7 +55,7 @@ ShenandoahGenerationalControlThread::ShenandoahGenerationalControlThread() : _heap(ShenandoahGenerationalHeap::heap()), _age_period(0) { shenandoah_assert_generational(); - set_name("Shenandoah Control Thread"); + set_name("ShenControl"); create_and_start(); } @@ -186,7 +186,7 @@ ShenandoahGenerationalControlThread::GCMode ShenandoahGenerationalControlThread: global_heuristics->record_requested_gc(); if (ShenandoahCollectorPolicy::should_run_full_gc(request.cause)) { - return stw_full;; + return stw_full; } else { // Unload and clean up everything. Note that this is an _explicit_ request and so does not use // the same `should_unload_classes` call as the regulator's concurrent gc request. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index b302bde8510..27e374a0f85 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -269,27 +269,25 @@ oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, uint break; } case OLD_GENERATION: { - PLAB* plab = ShenandoahThreadLocalData::plab(thread); - if (plab != nullptr) { + ShenandoahPLAB* shenandoah_plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + if (shenandoah_plab != nullptr) { has_plab = true; - copy = allocate_from_plab(thread, size, is_promotion); - if ((copy == nullptr) && (size < ShenandoahThreadLocalData::plab_size(thread)) && - ShenandoahThreadLocalData::plab_retries_enabled(thread)) { + copy = shenandoah_plab->allocate(size, is_promotion); + if (copy == nullptr && size < shenandoah_plab->desired_size() && shenandoah_plab->retries_enabled()) { // PLAB allocation failed because we are bumping up against the limit on old evacuation reserve or because // the requested object does not fit within the current plab but the plab still has an "abundance" of memory, // where abundance is defined as >= ShenGenHeap::plab_min_size(). In the former case, we try shrinking the // desired PLAB size to the minimum and retry PLAB allocation to avoid cascading of shared memory allocations. // Shrinking the desired PLAB size may allow us to eke out a small PLAB while staying beneath evacuation reserve. - if (plab->words_remaining() < plab_min_size()) { - ShenandoahThreadLocalData::set_plab_size(thread, plab_min_size()); - copy = allocate_from_plab(thread, size, is_promotion); - // If we still get nullptr, we'll try a shared allocation below. + if (shenandoah_plab->plab()->words_remaining() < plab_min_size()) { + shenandoah_plab->set_desired_size(plab_min_size()); + copy = shenandoah_plab->allocate(size, is_promotion); if (copy == nullptr) { - // If retry fails, don't continue to retry until we have success (probably in next GC pass) - ShenandoahThreadLocalData::disable_plab_retries(thread); + // If we still get nullptr, we'll try a shared allocation below. + // However, don't continue to retry until we have success (probably in next GC pass) + shenandoah_plab->disable_retries(); } } - // else, copy still equals nullptr. this causes shared allocation below, preserving this plab for future needs. } } break; @@ -384,9 +382,9 @@ oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, uint break; } case OLD_GENERATION: { - ShenandoahThreadLocalData::plab(thread)->undo_allocation(copy, size); + ShenandoahThreadLocalData::shenandoah_plab(thread)->plab()->undo_allocation(copy, size); if (is_promotion) { - ShenandoahThreadLocalData::subtract_from_plab_promoted(thread, size * HeapWordSize); + ShenandoahThreadLocalData::shenandoah_plab(thread)->subtract_from_promoted(size * HeapWordSize); } break; } @@ -411,179 +409,6 @@ template oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, uint from_region_age); template oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, uint from_region_age); -inline HeapWord* ShenandoahGenerationalHeap::allocate_from_plab(Thread* thread, size_t size, bool is_promotion) { - assert(UseTLAB, "TLABs should be enabled"); - - PLAB* plab = ShenandoahThreadLocalData::plab(thread); - HeapWord* obj; - - if (plab == nullptr) { - assert(!thread->is_Java_thread() && !thread->is_Worker_thread(), "Performance: thread should have PLAB: %s", thread->name()); - // No PLABs in this thread, fallback to shared allocation - return nullptr; - } else if (is_promotion && !ShenandoahThreadLocalData::allow_plab_promotions(thread)) { - return nullptr; - } - // if plab->word_size() <= 0, thread's plab not yet initialized for this pass, so allow_plab_promotions() is not trustworthy - obj = plab->allocate(size); - if ((obj == nullptr) && (plab->words_remaining() < plab_min_size())) { - // allocate_from_plab_slow will establish allow_plab_promotions(thread) for future invocations - obj = allocate_from_plab_slow(thread, size, is_promotion); - } - // if plab->words_remaining() >= ShenGenHeap::heap()->plab_min_size(), just return nullptr so we can use a shared allocation - if (obj == nullptr) { - return nullptr; - } - - if (is_promotion) { - ShenandoahThreadLocalData::add_to_plab_promoted(thread, size * HeapWordSize); - } - return obj; -} - -// Establish a new PLAB and allocate size HeapWords within it. -HeapWord* ShenandoahGenerationalHeap::allocate_from_plab_slow(Thread* thread, size_t size, bool is_promotion) { - assert(mode()->is_generational(), "PLABs only relevant to generational GC"); - - const size_t plab_min_size = this->plab_min_size(); - // PLABs are aligned to card boundaries to avoid synchronization with concurrent - // allocations in other PLABs. - const size_t min_size = (size > plab_min_size)? align_up(size, CardTable::card_size_in_words()): plab_min_size; - - // Figure out size of new PLAB, using value determined at last refill. - size_t cur_size = ShenandoahThreadLocalData::plab_size(thread); - if (cur_size == 0) { - cur_size = plab_min_size; - } - - // Expand aggressively, doubling at each refill in this epoch, ceiling at plab_max_size() - const size_t future_size = MIN2(cur_size * 2, plab_max_size()); - // Doubling, starting at a card-multiple, should give us a card-multiple. (Ceiling and floor - // are card multiples.) - assert(is_aligned(future_size, CardTable::card_size_in_words()), "Card multiple by construction, future_size: %zu" - ", card_size: %u, cur_size: %zu, max: %zu", - future_size, CardTable::card_size_in_words(), cur_size, plab_max_size()); - - // Record new heuristic value even if we take any shortcut. This captures - // the case when moderately-sized objects always take a shortcut. At some point, - // heuristics should catch up with them. Note that the requested cur_size may - // not be honored, but we remember that this is the preferred size. - log_debug(gc, plab)("Set next PLAB refill size: %zu bytes", future_size * HeapWordSize); - ShenandoahThreadLocalData::set_plab_size(thread, future_size); - - if (cur_size < size) { - // The PLAB to be allocated is still not large enough to hold the object. Fall back to shared allocation. - // This avoids retiring perfectly good PLABs in order to represent a single large object allocation. - log_debug(gc, plab)("Current PLAB size (%zu) is too small for %zu", cur_size * HeapWordSize, size * HeapWordSize); - return nullptr; - } - - // Retire current PLAB, and allocate a new one. - PLAB* plab = ShenandoahThreadLocalData::plab(thread); - if (plab->words_remaining() < plab_min_size) { - // Retire current PLAB. This takes care of any PLAB book-keeping. - // retire_plab() registers the remnant filler object with the remembered set scanner without a lock. - // Since PLABs are card-aligned, concurrent registrations in other PLABs don't interfere. - retire_plab(plab, thread); - - size_t actual_size = 0; - HeapWord* plab_buf = allocate_new_plab(min_size, cur_size, &actual_size); - if (plab_buf == nullptr) { - if (min_size == plab_min_size) { - // Disable PLAB promotions for this thread because we cannot even allocate a minimal PLAB. This allows us - // to fail faster on subsequent promotion attempts. - ShenandoahThreadLocalData::disable_plab_promotions(thread); - } - return nullptr; - } else { - ShenandoahThreadLocalData::enable_plab_retries(thread); - } - // Since the allocated PLAB may have been down-sized for alignment, plab->allocate(size) below may still fail. - if (ZeroTLAB) { - // ... and clear it. - Copy::zero_to_words(plab_buf, actual_size); - } else { - // ...and zap just allocated object. -#ifdef ASSERT - // Skip mangling the space corresponding to the object header to - // ensure that the returned space is not considered parsable by - // any concurrent GC thread. - size_t hdr_size = oopDesc::header_size(); - Copy::fill_to_words(plab_buf + hdr_size, actual_size - hdr_size, badHeapWordVal); -#endif // ASSERT - } - assert(is_aligned(actual_size, CardTable::card_size_in_words()), "Align by design"); - plab->set_buf(plab_buf, actual_size); - if (is_promotion && !ShenandoahThreadLocalData::allow_plab_promotions(thread)) { - return nullptr; - } - return plab->allocate(size); - } else { - // If there's still at least min_size() words available within the current plab, don't retire it. Let's nibble - // away on this plab as long as we can. Meanwhile, return nullptr to force this particular allocation request - // to be satisfied with a shared allocation. By packing more promotions into the previously allocated PLAB, we - // reduce the likelihood of evacuation failures, and we reduce the need for downsizing our PLABs. - return nullptr; - } -} - -HeapWord* ShenandoahGenerationalHeap::allocate_new_plab(size_t min_size, size_t word_size, size_t* actual_size) { - // Align requested sizes to card-sized multiples. Align down so that we don't violate max size of TLAB. - assert(is_aligned(min_size, CardTable::card_size_in_words()), "Align by design"); - assert(word_size >= min_size, "Requested PLAB is too small"); - - ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(min_size, word_size); - // Note that allocate_memory() sets a thread-local flag to prohibit further promotions by this thread - // if we are at risk of infringing on the old-gen evacuation budget. - HeapWord* res = allocate_memory(req); - if (res != nullptr) { - *actual_size = req.actual_size(); - } else { - *actual_size = 0; - } - assert(is_aligned(res, CardTable::card_size_in_words()), "Align by design"); - return res; -} - -void ShenandoahGenerationalHeap::retire_plab(PLAB* plab, Thread* thread) { - // We don't enforce limits on plab evacuations. We let it consume all available old-gen memory in order to reduce - // probability of an evacuation failure. We do enforce limits on promotion, to make sure that excessive promotion - // does not result in an old-gen evacuation failure. Note that a failed promotion is relatively harmless. Any - // object that fails to promote in the current cycle will be eligible for promotion in a subsequent cycle. - - // When the plab was instantiated, its entirety was treated as if the entire buffer was going to be dedicated to - // promotions. Now that we are retiring the buffer, we adjust for the reality that the plab is not entirely promotions. - // 1. Some of the plab may have been dedicated to evacuations. - // 2. Some of the plab may have been abandoned due to waste (at the end of the plab). - size_t not_promoted = - ShenandoahThreadLocalData::get_plab_actual_size(thread) - ShenandoahThreadLocalData::get_plab_promoted(thread); - ShenandoahThreadLocalData::reset_plab_promoted(thread); - ShenandoahThreadLocalData::set_plab_actual_size(thread, 0); - if (not_promoted > 0) { - log_debug(gc, plab)("Retire PLAB, unexpend unpromoted: %zu", not_promoted * HeapWordSize); - old_generation()->unexpend_promoted(not_promoted); - } - const size_t original_waste = plab->waste(); - HeapWord* const top = plab->top(); - - // plab->retire() overwrites unused memory between plab->top() and plab->hard_end() with a dummy object to make memory parsable. - // It adds the size of this unused memory, in words, to plab->waste(). - plab->retire(); - if (top != nullptr && plab->waste() > original_waste && is_in_old(top)) { - // If retiring the plab created a filler object, then we need to register it with our card scanner so it can - // safely walk the region backing the plab. - log_debug(gc, plab)("retire_plab() is registering remnant of size %zu at " PTR_FORMAT, - (plab->waste() - original_waste) * HeapWordSize, p2i(top)); - // No lock is necessary because the PLAB memory is aligned on card boundaries. - old_generation()->card_scan()->register_object_without_lock(top); - } -} - -void ShenandoahGenerationalHeap::retire_plab(PLAB* plab) { - Thread* thread = Thread::current(); - retire_plab(plab, thread); -} - // Make sure old-generation is large enough, but no larger than is necessary, to hold mixed evacuations // and promotions, if we anticipate either. Any deficit is provided by the young generation, subject to // mutator_xfer_limit, and any surplus is transferred to the young generation. mutator_xfer_limit is @@ -638,9 +463,13 @@ void ShenandoahGenerationalHeap::compute_old_generation_balance(size_t mutator_x bound_on_old_reserve: MIN2((young_reserve * ShenandoahOldEvacPercent) / (100 - ShenandoahOldEvacPercent), bound_on_old_reserve)); - if (young_reserve > young_available) { - young_reserve = young_available; + assert(mutator_xfer_limit <= young_available, + "Cannot transfer (%zu) memory that is not available (%zu)", mutator_xfer_limit, young_available); + // Young reserves are to be taken out of the mutator_xfer_limit. + if (young_reserve > mutator_xfer_limit) { + young_reserve = mutator_xfer_limit; } + mutator_xfer_limit -= young_reserve; // Decide how much old space we should reserve for a mixed collection size_t proposed_reserve_for_mixed = 0; @@ -1160,9 +989,7 @@ void ShenandoahGenerationalHeap::complete_degenerated_cycle() { coalesce_and_fill_old_regions(false); } - log_info(gc, cset)("Degenerated cycle complete, promotions reserved: %zu, promotions expended: %zu, failed count: %zu, failed bytes: %zu", - old_generation()->get_promoted_reserve(), old_generation()->get_promoted_expended(), - old_generation()->get_promotion_failed_count(), old_generation()->get_promotion_failed_words() * HeapWordSize); + old_generation()->maybe_log_promotion_failure_stats(false); } void ShenandoahGenerationalHeap::complete_concurrent_cycle() { @@ -1177,9 +1004,7 @@ void ShenandoahGenerationalHeap::complete_concurrent_cycle() { entry_global_coalesce_and_fill(); } - log_info(gc, cset)("Concurrent cycle complete, promotions reserved: %zu, promotions expended: %zu, failed count: %zu, failed bytes: %zu", - old_generation()->get_promoted_reserve(), old_generation()->get_promoted_expended(), - old_generation()->get_promotion_failed_count(), old_generation()->get_promotion_failed_words() * HeapWordSize); + old_generation()->maybe_log_promotion_failure_stats(true); } void ShenandoahGenerationalHeap::entry_global_coalesce_and_fill() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp index 7fe0362aa3f..d6893dc011e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp @@ -102,9 +102,6 @@ public: size_t plab_min_size() const { return _min_plab_size; } size_t plab_max_size() const { return _max_plab_size; } - void retire_plab(PLAB* plab); - void retire_plab(PLAB* plab, Thread* thread); - // ---------- Update References // // In the generational mode, we will use this function for young, mixed, and global collections. @@ -113,10 +110,6 @@ public: void final_update_refs_update_region_states() override; private: - HeapWord* allocate_from_plab(Thread* thread, size_t size, bool is_promotion); - HeapWord* allocate_from_plab_slow(Thread* thread, size_t size, bool is_promotion); - HeapWord* allocate_new_plab(size_t min_size, size_t word_size, size_t* actual_size); - const size_t _min_plab_size; const size_t _max_plab_size; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index c6889351161..817f5e8dc47 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -261,7 +261,7 @@ jint ShenandoahHeap::initialize() { // // Worker threads must be initialized after the barrier is configured // - _workers = new ShenandoahWorkerThreads("Shenandoah GC Threads", _max_workers); + _workers = new ShenandoahWorkerThreads("ShenWorker", _max_workers); if (_workers == nullptr) { vm_exit_during_initialization("Failed necessary allocation."); } else { @@ -1182,20 +1182,20 @@ public: } if (ShenandoahHeap::heap()->mode()->is_generational()) { - PLAB* plab = ShenandoahThreadLocalData::plab(thread); - assert(plab != nullptr, "PLAB should be initialized for %s", thread->name()); + ShenandoahPLAB* shenandoah_plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + assert(shenandoah_plab != nullptr, "PLAB should be initialized for %s", thread->name()); // There are two reasons to retire all plabs between old-gen evacuation passes. // 1. We need to make the plab memory parsable by remembered-set scanning. // 2. We need to establish a trustworthy UpdateWaterMark value within each old-gen heap region - ShenandoahGenerationalHeap::heap()->retire_plab(plab, thread); + shenandoah_plab->retire(); // Re-enable promotions for the next evacuation phase. - ShenandoahThreadLocalData::enable_plab_promotions(thread); + shenandoah_plab->enable_promotions(); // Reset the fill size for next evacuation phase. - if (_resize && ShenandoahThreadLocalData::plab_size(thread) > 0) { - ShenandoahThreadLocalData::set_plab_size(thread, 0); + if (_resize && shenandoah_plab->desired_size() > 0) { + shenandoah_plab->set_desired_size(0); } } } @@ -1465,9 +1465,9 @@ public: assert(gclab->words_remaining() == 0, "GCLAB should not need retirement"); if (ShenandoahHeap::heap()->mode()->is_generational()) { - PLAB* plab = ShenandoahThreadLocalData::plab(thread); - assert(plab != nullptr, "PLAB should be initialized for %s", thread->name()); - assert(plab->words_remaining() == 0, "PLAB should not need retirement"); + ShenandoahPLAB* shenandoah_plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + assert(shenandoah_plab != nullptr, "PLAB should be initialized for %s", thread->name()); + assert(shenandoah_plab->plab()->words_remaining() == 0, "PLAB should not need retirement"); } } }; @@ -2700,10 +2700,7 @@ GrowableArray ShenandoahHeap::memory_pools() { } MemoryUsage ShenandoahHeap::memory_usage() { - assert(_initial_size <= ShenandoahHeap::heap()->max_capacity(), "sanity"); - assert(used() <= ShenandoahHeap::heap()->max_capacity(), "sanity"); - assert(committed() <= ShenandoahHeap::heap()->max_capacity(), "sanity"); - return MemoryUsage(_initial_size, used(), committed(), max_capacity()); + return shenandoah_memory_usage(_initial_size, used(), committed(), max_capacity()); } ShenandoahRegionIterator::ShenandoahRegionIterator() : diff --git a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp index 83f4217df83..10d61221e87 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp @@ -26,38 +26,11 @@ #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" -#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "gc/shenandoah/shenandoahInPlacePromoter.hpp" #include "gc/shenandoah/shenandoahMarkingContext.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahYoungGeneration.hpp" -ShenandoahInPlacePromotionPlanner::RegionPromotions::RegionPromotions(ShenandoahFreeSet* free_set) - : _low_idx(free_set->max_regions()) - , _high_idx(-1) - , _regions(0) - , _bytes(0) - , _free_set(free_set) -{ -} - -void ShenandoahInPlacePromotionPlanner::RegionPromotions::increment(idx_t region_index, size_t remnant_bytes) { - if (region_index < _low_idx) { - _low_idx = region_index; - } - if (region_index > _high_idx) { - _high_idx = region_index; - } - _regions++; - _bytes += remnant_bytes; -} - -void ShenandoahInPlacePromotionPlanner::RegionPromotions::update_free_set(ShenandoahFreeSetPartitionId partition_id) const { - if (_regions > 0) { - _free_set->shrink_interval_if_range_modifies_either_boundary(partition_id, _low_idx, _high_idx, _regions); - } -} - ShenandoahInPlacePromotionPlanner::ShenandoahInPlacePromotionPlanner(const ShenandoahGenerationalHeap* heap) : _old_garbage_threshold(ShenandoahHeapRegion::region_size_bytes() * heap->old_generation()->heuristics()->get_old_garbage_threshold() / 100) , _pip_used_threshold(ShenandoahHeapRegion::region_size_bytes() * ShenandoahGenerationalMinPIPUsage / 100) @@ -75,6 +48,7 @@ bool ShenandoahInPlacePromotionPlanner::is_eligible(const ShenandoahHeapRegion* } void ShenandoahInPlacePromotionPlanner::prepare(ShenandoahHeapRegion* r) { + assert(!r->is_humongous_continuation(), "Should not call for humongous continuations"); HeapWord* tams = _marking_context->top_at_mark_start(r); HeapWord* original_top = r->top(); @@ -86,6 +60,20 @@ void ShenandoahInPlacePromotionPlanner::prepare(ShenandoahHeapRegion* r) { return; } + if (r->is_humongous_start()) { + if (const oop obj = cast_to_oop(r->bottom()); !obj->is_typeArray()) { + // Nothing else to do for humongous, we just update the stats and move on. The humongous regions + // themselves will be discovered and promoted by gc workers during evacuation. Note that humongous + // primitive arrays are not promoted. + const size_t num_regions = ShenandoahHeapRegion::required_regions(obj->size() * HeapWordSize); + for (size_t i = r->index(); i < r->index() + num_regions; i++) { + _pip_humongous_stats.update(_heap->get_region(i)); + } + } + return; + } + + _pip_regular_stats.update(r); // No allocations from this region have been made during concurrent mark. It meets all the criteria // for in-place-promotion. Though we only need the value of top when we fill the end of the region, // we use this field to indicate that this region should be promoted in place during the evacuation @@ -128,8 +116,14 @@ void ShenandoahInPlacePromotionPlanner::prepare(ShenandoahHeapRegion* r) { } } -void ShenandoahInPlacePromotionPlanner::update_free_set() const { +void ShenandoahInPlacePromotionPlanner::complete_planning() const { _heap->old_generation()->set_pad_for_promote_in_place(_pip_padding_bytes); + _heap->old_generation()->set_expected_humongous_region_promotions(_pip_humongous_stats.count); + _heap->old_generation()->set_expected_regular_region_promotions(_pip_regular_stats.count); + log_info(gc, ergo)("Planning to promote in place %zu humongous regions and %zu" + " regular regions, spanning a total of %zu used bytes", + _pip_humongous_stats.count, _pip_regular_stats.count, + _pip_humongous_stats.usage + _pip_regular_stats.usage); if (_mutator_regions._regions + _collector_regions._regions > 0) { _free_set->account_for_pip_regions(_mutator_regions._regions, _mutator_regions._bytes, diff --git a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.hpp b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.hpp index 939107dd3ac..d2cb644a59e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.hpp @@ -25,16 +25,21 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHINPLACEPROMOTER_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHINPLACEPROMOTER_HPP +#include "gc/shenandoah/shenandoahFreeSet.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "gc/shenandoah/shenandoahSimpleBitMap.hpp" -class ShenandoahFreeSet; class ShenandoahMarkingContext; class ShenandoahGenerationalHeap; -class ShenandoahHeapRegion; +// This class is responsible for identifying regions that can be +// promoted in place. It also prepares these regions by preventing +// them from being used for allocations. Finally, it notifies the +// freeset which regions are to be promoted in place. class ShenandoahInPlacePromotionPlanner { using idx_t = ShenandoahSimpleBitMap::idx_t; + // Used to inform free set of regions being promoted struct RegionPromotions { idx_t _low_idx; idx_t _high_idx; @@ -42,9 +47,47 @@ class ShenandoahInPlacePromotionPlanner { size_t _bytes; ShenandoahFreeSet* _free_set; - explicit RegionPromotions(ShenandoahFreeSet* free_set); - void increment(idx_t region_index, size_t remnant_bytes); - void update_free_set(ShenandoahFreeSetPartitionId partition_id) const; + explicit RegionPromotions(ShenandoahFreeSet* free_set) + : _low_idx(free_set->max_regions()) + , _high_idx(-1) + , _regions(0) + , _bytes(0) + , _free_set(free_set) + { + } + + void increment(idx_t region_index, size_t remnant_bytes) { + if (region_index < _low_idx) { + _low_idx = region_index; + } + if (region_index > _high_idx) { + _high_idx = region_index; + } + _regions++; + _bytes += remnant_bytes; + } + + void update_free_set(ShenandoahFreeSetPartitionId partition_id) const { + if (_regions > 0) { + _free_set->shrink_interval_if_range_modifies_either_boundary(partition_id, _low_idx, _high_idx, _regions); + } + } + }; + + // Used to track metrics about the regions being promoted in place + struct RegionPromotionStats { + size_t count; + size_t usage; + size_t free; + size_t garbage; + + RegionPromotionStats() : count(0), usage(0), free(0), garbage(0) {} + void update(ShenandoahHeapRegion* region) { + count++; + usage += region->used(); + free += region->free(); + garbage += region->garbage(); + } }; const size_t _old_garbage_threshold; @@ -60,6 +103,11 @@ class ShenandoahInPlacePromotionPlanner { // Tracks the padding of space above top in regions eligible for promotion in place size_t _pip_padding_bytes; + + // Tracks stats for in place promotions + RegionPromotionStats _pip_regular_stats; + RegionPromotionStats _pip_humongous_stats; + public: explicit ShenandoahInPlacePromotionPlanner(const ShenandoahGenerationalHeap* heap); @@ -69,12 +117,17 @@ public: // Prepares the region for promotion by moving top to the end to prevent allocations void prepare(ShenandoahHeapRegion* region); - // Notifies the free set of in place promotions - void update_free_set() const; + // Notifies the free set and old generation of in place promotions + void complete_planning() const; + + const RegionPromotionStats& regular_region_stats() const { return _pip_regular_stats; } + const RegionPromotionStats& humongous_region_stats() const { return _pip_humongous_stats; } size_t old_garbage_threshold() const { return _old_garbage_threshold; } }; +// For regions that have been selected and prepared for promotion, this class +// will perform the actual promotion. class ShenandoahInPlacePromoter { ShenandoahGenerationalHeap* _heap; public: @@ -89,3 +142,4 @@ private: }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHINPLACEPROMOTER_HPP + diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.cpp index d55d1bd8147..dfc34dcc3c3 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.cpp @@ -48,25 +48,7 @@ ShenandoahMemoryPool::ShenandoahMemoryPool(ShenandoahHeap* heap, MemoryUsage ShenandoahMemoryPool::get_memory_usage() { - size_t initial = initial_size(); - size_t max = max_size(); - size_t used = used_in_bytes(); - size_t committed = _heap->committed(); - - // These asserts can never fail: max is stable, and all updates to other values never overflow max. - assert(initial <= max, "initial: %zu, max: %zu", initial, max); - assert(used <= max, "used: %zu, max: %zu", used, max); - assert(committed <= max, "committed: %zu, max: %zu", committed, max); - - // Committed and used are updated concurrently and independently. They can momentarily break - // the assert below, which would also fail in downstream code. To avoid that, adjust values - // to make sense under the race. See JDK-8207200. - committed = MAX2(used, committed); - assert(used <= committed, "used: %zu, committed: %zu", used, committed); - assert(initial <= _heap->max_capacity(), "sanity"); - assert(committed <= _heap->max_capacity(), "sanity"); - assert(max <= _heap->max_capacity(), "sanity"); - return MemoryUsage(initial, used, committed, max); + return shenandoah_memory_usage(initial_size(), used_in_bytes(), _heap->committed(), max_size()); } size_t ShenandoahMemoryPool::used_in_bytes() { @@ -83,16 +65,7 @@ ShenandoahGenerationalMemoryPool::ShenandoahGenerationalMemoryPool(ShenandoahHea _generation(generation) { } MemoryUsage ShenandoahGenerationalMemoryPool::get_memory_usage() { - size_t initial = initial_size(); - size_t max = max_size(); - size_t used = used_in_bytes(); - size_t committed = _generation->used_regions_size(); - - assert(initial <= _heap->max_capacity(), "sanity"); - assert(used <= _heap->max_capacity(), "sanity"); - assert(committed <= _heap->max_capacity(), "sanity"); - assert(max <= _heap->max_capacity(), "sanity"); - return MemoryUsage(initial, used, committed, max); + return shenandoah_memory_usage(initial_size(), used_in_bytes(), _generation->used_regions_size(), max_size()); } size_t ShenandoahGenerationalMemoryPool::used_in_bytes() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.hpp index ccdfdddede9..d466087b9b7 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.hpp @@ -30,6 +30,17 @@ #include "services/memoryPool.hpp" #include "services/memoryUsage.hpp" +// Constructs a MemoryUsage from concurrently sampled values, clamping committed +// to be at least as large as used to account for concurrent updates. See JDK-8207200. +inline MemoryUsage shenandoah_memory_usage(size_t initial, size_t used, size_t committed, size_t max) { + assert(initial <= max, "initial: %zu, max: %zu", initial, max); + assert(used <= max, "used: %zu, max: %zu", used, max); + assert(committed <= max, "committed: %zu, max: %zu", committed, max); + committed = MAX2(used, committed); + assert(used <= committed, "used: %zu, committed: %zu", used, committed); + return MemoryUsage(initial, used, committed, max); +} + class ShenandoahMemoryPool : public CollectedMemoryPool { protected: ShenandoahHeap* _heap; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index 1b12909bcaf..16a24da8b1c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -38,6 +38,7 @@ #include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" +#include "gc/shenandoah/shenandoahThreadLocalData.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shenandoah/shenandoahWorkerPolicy.hpp" #include "gc/shenandoah/shenandoahYoungGeneration.hpp" @@ -109,8 +110,6 @@ ShenandoahOldGeneration::ShenandoahOldGeneration(uint max_queues) _promoted_expended(0), _promotion_potential(0), _pad_for_promote_in_place(0), - _promotion_failure_count(0), - _promotion_failure_words(0), _promotable_humongous_regions(0), _promotable_regular_regions(0), _is_parsable(true), @@ -148,8 +147,50 @@ void ShenandoahOldGeneration::augment_promoted_reserve(size_t increment) { void ShenandoahOldGeneration::reset_promoted_expended() { shenandoah_assert_heaplocked_or_safepoint(); _promoted_expended.store_relaxed(0); - _promotion_failure_count.store_relaxed(0); - _promotion_failure_words.store_relaxed(0); +} + +void ShenandoahOldGeneration::maybe_log_promotion_failure_stats(bool concurrent) const { + LogTarget(Info, gc, plab) plab_info; + if (plab_info.is_enabled()) { + size_t failed_count = 0; + size_t failed_words = 0; + + class AggregatePromotionFailuresClosure : public ThreadClosure { + private: + size_t _total_count; + size_t _total_words; + public: + AggregatePromotionFailuresClosure() : _total_count(0), _total_words(0) {} + + void do_thread(Thread* thread) override { + ShenandoahPLAB* plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + if (plab != nullptr) { + _total_count += plab->get_promotion_failure_count(); + _total_words += plab->get_promotion_failure_words(); + plab->reset_promotion_failures(); + } + } + + size_t total_count() const { return _total_count; } + size_t total_words() const { return _total_words; } + }; + + AggregatePromotionFailuresClosure cl; + if (concurrent) { + MutexLocker lock(Threads_lock); + Threads::threads_do(&cl); + } else { + Threads::threads_do(&cl); + } + + failed_count = cl.total_count(); + failed_words = cl.total_words(); + + LogStream ls(plab_info); + ls.print_cr("Cycle complete, promotions reserved: %zu, promotions expended: %zu, failed count: %zu, failed bytes: %zu", + get_promoted_reserve(), get_promoted_expended(), + failed_count, failed_words * HeapWordSize); + } } size_t ShenandoahOldGeneration::expend_promoted(size_t increment) { @@ -199,7 +240,8 @@ ShenandoahOldGeneration::configure_plab_for_current_thread(const ShenandoahAlloc // We've created a new plab. Now we configure it whether it will be used for promotions // and evacuations - or just evacuations. Thread* thread = Thread::current(); - ShenandoahThreadLocalData::reset_plab_promoted(thread); + ShenandoahPLAB* shenandoah_plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + shenandoah_plab->reset_promoted(); // The actual size of the allocation may be larger than the requested bytes (due to alignment on card boundaries). // If this puts us over our promotion budget, we need to disable future PLAB promotions for this thread. @@ -209,12 +251,12 @@ ShenandoahOldGeneration::configure_plab_for_current_thread(const ShenandoahAlloc log_debug(gc, plab)("Thread can promote using PLAB of %zu bytes. Expended: %zu, available: %zu", actual_size, get_promoted_expended(), get_promoted_reserve()); expend_promoted(actual_size); - ShenandoahThreadLocalData::enable_plab_promotions(thread); - ShenandoahThreadLocalData::set_plab_actual_size(thread, actual_size); + shenandoah_plab->enable_promotions(); + shenandoah_plab->set_actual_size(actual_size); } else { // Disable promotions in this thread because entirety of this PLAB must be available to hold old-gen evacuations. - ShenandoahThreadLocalData::disable_plab_promotions(thread); - ShenandoahThreadLocalData::set_plab_actual_size(thread, 0); + shenandoah_plab->disable_promotions(); + shenandoah_plab->set_actual_size(0); log_debug(gc, plab)("Thread cannot promote using PLAB of %zu bytes. Expended: %zu, available: %zu, mixed evacuations? %s", actual_size, get_promoted_expended(), get_promoted_reserve(), BOOL_TO_STR(ShenandoahHeap::heap()->collection_set()->has_old_regions())); } @@ -582,13 +624,21 @@ void ShenandoahOldGeneration::handle_failed_evacuation() { } } -void ShenandoahOldGeneration::handle_failed_promotion(Thread* thread, size_t size) { - _promotion_failure_count.add_then_fetch(1UL); - _promotion_failure_words.add_then_fetch(size); +void ShenandoahOldGeneration::handle_failed_promotion(Thread* thread, size_t size) const { + LogTarget(Info, gc, plab) plab_info; + if (plab_info.is_enabled()) { + ShenandoahPLAB* plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + if (plab != nullptr) { + plab->record_promotion_failure(size); + } else { + ResourceMark for_thread_name; + log_debug(gc, plab)("Thread: %s has no plab", thread->name()); + } + } - LogTarget(Debug, gc, plab) lt; - LogStream ls(lt); - if (lt.is_enabled()) { + LogTarget(Debug, gc, plab) plab_debug; + if (plab_debug.is_enabled()) { + LogStream ls(plab_debug); log_failed_promotion(ls, thread, size); } } @@ -603,9 +653,10 @@ void ShenandoahOldGeneration::log_failed_promotion(LogStream& ls, Thread* thread const size_t gc_id = heap->control_thread()->get_gc_id(); if ((gc_id != last_report_epoch) || (epoch_report_count++ < MaxReportsPerEpoch)) { // Promotion failures should be very rare. Invest in providing useful diagnostic info. - PLAB* const plab = ShenandoahThreadLocalData::plab(thread); + ShenandoahPLAB* const shenandoah_plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + PLAB* const plab = (shenandoah_plab == nullptr)? nullptr: shenandoah_plab->plab(); const size_t words_remaining = (plab == nullptr)? 0: plab->words_remaining(); - const char* promote_enabled = ShenandoahThreadLocalData::allow_plab_promotions(thread)? "enabled": "disabled"; + const char* promote_enabled = (shenandoah_plab != nullptr && shenandoah_plab->allows_promotion())? "enabled": "disabled"; // Promoted reserve is only changed by vm or control thread. Promoted expended is always accessed atomically. const size_t promotion_reserve = get_promoted_reserve(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp index 5ebad461f3c..12e046a1afc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp @@ -76,11 +76,6 @@ private: // objects). This field records the total amount of padding used for such regions. size_t _pad_for_promote_in_place; - // Keep track of the number and size of promotions that failed. Perhaps we should use this to increase - // the size of the old generation for the next collection cycle. - Atomic _promotion_failure_count; - Atomic _promotion_failure_words; - // During construction of the collection set, we keep track of regions that are eligible // for promotion in place. These fields track the count of those humongous and regular regions. // This data is used to force the evacuation phase even when the collection set is otherwise @@ -125,9 +120,8 @@ public: // This is used on the allocation path to gate promotions that would exceed the reserve size_t get_promoted_expended() const; - // Return the count and size (in words) of failed promotions since the last reset - size_t get_promotion_failed_count() const { return _promotion_failure_count.load_relaxed(); } - size_t get_promotion_failed_words() const { return _promotion_failure_words.load_relaxed(); } + // Aggregate and log promotion failure stats if logging is enabled + void maybe_log_promotion_failure_stats(bool concurrent) const; // Test if there is enough memory reserved for this promotion bool can_promote(size_t requested_bytes) const { @@ -175,7 +169,7 @@ public: void handle_failed_evacuation(); // Increment promotion failure counters, optionally log a more detailed message - void handle_failed_promotion(Thread* thread, size_t size); + void handle_failed_promotion(Thread* thread, size_t size) const; void log_failed_promotion(LogStream& ls, Thread* thread, size_t size) const; // A successful evacuation re-dirties the cards and registers the object with the remembered set diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPLAB.cpp b/src/hotspot/share/gc/shenandoah/shenandoahPLAB.cpp new file mode 100644 index 00000000000..412cfa9447e --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahPLAB.cpp @@ -0,0 +1,221 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#include "gc/shared/cardTable.hpp" +#include "gc/shenandoah/shenandoahAllocRequest.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahPLAB.hpp" +#include "logging/log.hpp" +#include "runtime/globals.hpp" +#include "runtime/javaThread.hpp" +#include "utilities/copy.hpp" + +ShenandoahPLAB::ShenandoahPLAB() : + _plab(nullptr), + _desired_size(0), + _actual_size(0), + _promoted(0), + _promotion_failure_count(0), + _promotion_failure_words(0), + _allows_promotion(false), + _retries_enabled(false), + _heap(ShenandoahGenerationalHeap::heap()) { + _plab = new PLAB(align_up(PLAB::min_size(), CardTable::card_size_in_words())); +} + +ShenandoahPLAB::~ShenandoahPLAB() { + if (_plab != nullptr) { + delete _plab; + } +} + +void ShenandoahPLAB::subtract_from_promoted(size_t increment) { + assert(_promoted >= increment, "Cannot subtract more than remaining promoted"); + _promoted -= increment; +} + +HeapWord* ShenandoahPLAB::allocate(size_t size, bool is_promotion) { + assert(UseTLAB, "TLABs should be enabled"); + + if (_plab == nullptr) { + // No PLABs in this thread, fallback to shared allocation + return nullptr; + } + + if (is_promotion && !_allows_promotion) { + // Thread is not allowed to promote + return nullptr; + } + + HeapWord* obj = _plab->allocate(size); + if (obj == nullptr) { + if (_plab->words_remaining() < _heap->plab_min_size()) { + // allocate_slow will establish _allows_promotion for future invocations + obj = allocate_slow(size, is_promotion); + } + } + + // if plab->words_remaining() >= ShenGenHeap::heap()->plab_min_size(), just return nullptr so we can use a shared allocation + if (obj == nullptr) { + return nullptr; + } + + if (is_promotion) { + add_to_promoted(size * HeapWordSize); + } + return obj; +} + +// Establish a new PLAB and allocate size HeapWords within it. +HeapWord* ShenandoahPLAB::allocate_slow(size_t size, bool is_promotion) { + assert(_heap->mode()->is_generational(), "PLABs only relevant to generational GC"); + + // PLABs are aligned to card boundaries to avoid synchronization with concurrent + // allocations in other PLABs. + const size_t plab_min_size = _heap->plab_min_size(); + const size_t min_size = (size > plab_min_size)? align_up(size, CardTable::card_size_in_words()): plab_min_size; + + // Figure out size of new PLAB, using value determined at last refill. + size_t cur_size = _desired_size; + if (cur_size == 0) { + cur_size = plab_min_size; + } + + // Expand aggressively, doubling at each refill in this epoch, ceiling at plab_max_size() + // Doubling, starting at a card-multiple, should give us a card-multiple. (Ceiling and floor + // are card multiples.) + const size_t future_size = MIN2(cur_size * 2, _heap->plab_max_size()); + assert(is_aligned(future_size, CardTable::card_size_in_words()), "Card multiple by construction, future_size: %zu" + ", card_size: %u, cur_size: %zu, max: %zu", + future_size, CardTable::card_size_in_words(), cur_size, _heap->plab_max_size()); + + // Record new heuristic value even if we take any shortcut. This captures + // the case when moderately-sized objects always take a shortcut. At some point, + // heuristics should catch up with them. Note that the requested cur_size may + // not be honored, but we remember that this is the preferred size. + log_debug(gc, plab)("Set next PLAB refill size: %zu bytes", future_size * HeapWordSize); + set_desired_size(future_size); + + if (cur_size < size) { + // The PLAB to be allocated is still not large enough to hold the object. Fall back to shared allocation. + // This avoids retiring perfectly good PLABs in order to represent a single large object allocation. + log_debug(gc, plab)("Current PLAB size (%zu) is too small for %zu", cur_size * HeapWordSize, size * HeapWordSize); + return nullptr; + } + + if (_plab->words_remaining() < plab_min_size) { + // Retire current PLAB. This takes care of any PLAB book-keeping. + // retire_plab() registers the remnant filler object with the remembered set scanner without a lock. + // Since PLABs are card-aligned, concurrent registrations in other PLABs don't interfere. + retire(); + + size_t actual_size = 0; + HeapWord* plab_buf = allocate_new_plab(min_size, cur_size, &actual_size); + if (plab_buf == nullptr) { + if (min_size == plab_min_size) { + // Disable PLAB promotions for this thread because we cannot even allocate a minimal PLAB. This allows us + // to fail faster on subsequent promotion attempts. + disable_promotions(); + } + return nullptr; + } + + enable_retries(); + + // Since the allocated PLAB may have been down-sized for alignment, plab->allocate(size) below may still fail. + if (ZeroTLAB) { + // Skip mangling the space corresponding to the object header to + // ensure that the returned space is not considered parsable by + // any concurrent GC thread. + Copy::zero_to_words(plab_buf, actual_size); + } else { +#ifdef ASSERT + size_t hdr_size = oopDesc::header_size(); + Copy::fill_to_words(plab_buf + hdr_size, actual_size - hdr_size, badHeapWordVal); +#endif + } + assert(is_aligned(actual_size, CardTable::card_size_in_words()), "Align by design"); + _plab->set_buf(plab_buf, actual_size); + if (is_promotion && !_allows_promotion) { + return nullptr; + } + return _plab->allocate(size); + } + + // If there's still at least min_size() words available within the current plab, don't retire it. Let's nibble + // away on this plab as long as we can. Meanwhile, return nullptr to force this particular allocation request + // to be satisfied with a shared allocation. By packing more promotions into the previously allocated PLAB, we + // reduce the likelihood of evacuation failures, and we reduce the need for downsizing our PLABs. + return nullptr; +} + +HeapWord* ShenandoahPLAB::allocate_new_plab(size_t min_size, size_t word_size, size_t* actual_size) { + assert(is_aligned(min_size, CardTable::card_size_in_words()), "Align by design"); + assert(word_size >= min_size, "Requested PLAB is too small"); + + ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(min_size, word_size); + HeapWord* res = _heap->allocate_memory(req); + if (res != nullptr) { + *actual_size = req.actual_size(); + } else { + *actual_size = 0; + } + assert(is_aligned(res, CardTable::card_size_in_words()), "Align by design"); + return res; +} + +void ShenandoahPLAB::retire() { + // We don't enforce limits on plab evacuations. We let it consume all available old-gen memory in order to reduce + // probability of an evacuation failure. We do enforce limits on promotion, to make sure that excessive promotion + // does not result in an old-gen evacuation failure. Note that a failed promotion is relatively harmless. Any + // object that fails to promote in the current cycle will be eligible for promotion in a subsequent cycle. + + // When the plab was instantiated, its entirety was treated as if the entire buffer was going to be dedicated to + // promotions. Now that we are retiring the buffer, we adjust for the reality that the plab is not entirely promotions. + // 1. Some of the plab may have been dedicated to evacuations. + // 2. Some of the plab may have been abandoned due to waste (at the end of the plab). + size_t not_promoted = _actual_size - _promoted; + reset_promoted(); + set_actual_size(0); + if (not_promoted > 0) { + log_debug(gc, plab)("Retire PLAB, unexpend unpromoted: %zu", not_promoted * HeapWordSize); + _heap->old_generation()->unexpend_promoted(not_promoted); + } + const size_t original_waste = _plab->waste(); + HeapWord* const top = _plab->top(); + + // plab->retire() overwrites unused memory between plab->top() and plab->hard_end() with a dummy object to make memory parsable. + // It adds the size of this unused memory, in words, to plab->waste(). + _plab->retire(); + if (top != nullptr && _plab->waste() > original_waste && _heap->is_in_old(top)) { + // If retiring the plab created a filler object, then we need to register it with our card scanner so it can + // safely walk the region backing the plab. + log_debug(gc, plab)("retire_plab() is registering remnant of size %zu at " PTR_FORMAT, + (_plab->waste() - original_waste) * HeapWordSize, p2i(top)); + // No lock is necessary because the PLAB memory is aligned on card boundaries. + _heap->old_generation()->card_scan()->register_object_without_lock(top); + } +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPLAB.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPLAB.hpp new file mode 100644 index 00000000000..4278eb65b10 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahPLAB.hpp @@ -0,0 +1,130 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHPLAB_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHPLAB_HPP + +#include "gc/shared/plab.hpp" +#include "memory/allocation.hpp" + +class ShenandoahGenerationalHeap; + +class ShenandoahPLAB : public CHeapObj { +private: + // The actual allocation buffer + PLAB* _plab; + + // Heuristics will grow the desired size of plabs. + size_t _desired_size; + + // Once the plab has been allocated, and we know the actual size, we record it here. + size_t _actual_size; + + // As the plab is used for promotions, this value is incremented. When the plab is + // retired, the difference between 'actual_size' and 'promoted' will be returned to + // the old generation's promotion reserve (i.e., it will be 'unexpended'). + size_t _promoted; + + // Track failed promotion attempts per thread + size_t _promotion_failure_count; + size_t _promotion_failure_words; + + // If false, no more promotion by this thread during this evacuation phase. + bool _allows_promotion; + + // If true, evacuations may attempt to allocate a smaller plab if the original size fails. + bool _retries_enabled; + + // Use for allocations, min/max plab sizes + ShenandoahGenerationalHeap* _heap; + + // Enable retry logic for PLAB allocation failures + void enable_retries() { _retries_enabled = true; } + + // Establish a new PLAB and allocate from it + HeapWord* allocate_slow(size_t size, bool is_promotion); + // Allocate a new PLAB buffer from the heap + HeapWord* allocate_new_plab(size_t min_size, size_t word_size, size_t* actual_size); + +public: + ShenandoahPLAB(); + ~ShenandoahPLAB(); + + // Access the underlying PLAB buffer + PLAB* plab() const { return _plab; } + + // Heuristic size for next PLAB allocation + size_t desired_size() const { return _desired_size; } + // Update heuristic size for next PLAB allocation + void set_desired_size(size_t v) { _desired_size = v; } + + // Check if retry logic is enabled + bool retries_enabled() const { return _retries_enabled; } + // Disable retry logic for PLAB allocation failures + void disable_retries() { _retries_enabled = false; } + + // Allow this thread to promote objects + void enable_promotions() { _allows_promotion = true; } + // Prevent this thread from promoting objects + void disable_promotions() { _allows_promotion = false; } + // Check if this thread is allowed to promote objects + bool allows_promotion() const { return _allows_promotion; } + + // Reset promotion tracking for new evacuation phase + void reset_promoted() { _promoted = 0; } + // When a plab is retired, subtract from the expended promotion budget + void subtract_from_promoted(size_t increment); + // Bytes promoted through this PLAB + size_t get_promoted() const { return _promoted; } + // Track promoted bytes in this PLAB + void add_to_promoted(size_t increment) { _promoted += increment; } + + // Track failed promotion attempts + void record_promotion_failure(size_t size) { + _promotion_failure_count++; + _promotion_failure_words += size; + } + // Get failed promotion count for aggregation + size_t get_promotion_failure_count() const { return _promotion_failure_count; } + // Get failed promotion words for aggregation + size_t get_promotion_failure_words() const { return _promotion_failure_words; } + // Reset failure tracking for new evacuation phase + void reset_promotion_failures() { + _promotion_failure_count = 0; + _promotion_failure_words = 0; + } + + // Record actual allocated PLAB size + void set_actual_size(size_t value) { _actual_size = value; } + // Actual allocated PLAB size + size_t get_actual_size() const { return _actual_size; } + + // Allocate from this PLAB + HeapWord* allocate(size_t size, bool is_promotion); + + // Retire this PLAB and return unused promotion budget + void retire(); +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHPLAB_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp index fe92a3a3e08..28094a1d57d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp @@ -43,7 +43,7 @@ ShenandoahRegulatorThread::ShenandoahRegulatorThread(ShenandoahGenerationalContr _young_heuristics = _heap->young_generation()->heuristics(); _global_heuristics = _heap->global_generation()->heuristics(); - set_name("Shenandoah Regulator Thread"); + set_name("ShenRegulator"); create_and_start(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp index 42db0b2fd2c..dbae1b35c6f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2016, 2024, Red Hat, Inc. All rights reserved. - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -320,7 +320,7 @@ public: DEBUG_ONLY(_reserved = 0; ) } - void clear_claimed() { _claimed_index = 0; } + void clear_claimed() { _claimed_index.store_relaxed(0); } T* claim_next(); // reserve queues that not for parallel claiming diff --git a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp index ace5ab5e69a..1f3ce76cc1c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp @@ -37,12 +37,7 @@ ShenandoahThreadLocalData::ShenandoahThreadLocalData() : _card_table(nullptr), _gclab(nullptr), _gclab_size(0), - _plab(nullptr), - _plab_desired_size(0), - _plab_actual_size(0), - _plab_promoted(0), - _plab_allows_promotion(true), - _plab_retries_enabled(true), + _shenandoah_plab(nullptr), _evacuation_stats(new ShenandoahEvacuationStats()) { } @@ -50,9 +45,9 @@ ShenandoahThreadLocalData::~ShenandoahThreadLocalData() { if (_gclab != nullptr) { delete _gclab; } - if (_plab != nullptr) { - ShenandoahGenerationalHeap::heap()->retire_plab(_plab); - delete _plab; + if (_shenandoah_plab != nullptr) { + _shenandoah_plab->retire(); + delete _shenandoah_plab; } delete _evacuation_stats; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp index f54a65b0785..b1b923bbfce 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp @@ -36,6 +36,7 @@ #include "gc/shenandoah/shenandoahCodeRoots.hpp" #include "gc/shenandoah/shenandoahEvacTracker.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahPLAB.hpp" #include "gc/shenandoah/shenandoahSATBMarkQueueSet.hpp" #include "runtime/javaThread.hpp" #include "utilities/debug.hpp" @@ -62,24 +63,7 @@ private: // Used both by mutator threads and by GC worker threads // for evacuations within the old generation and // for promotions from the young generation into the old generation. - PLAB* _plab; - - // Heuristics will grow the desired size of plabs. - size_t _plab_desired_size; - - // Once the plab has been allocated, and we know the actual size, we record it here. - size_t _plab_actual_size; - - // As the plab is used for promotions, this value is incremented. When the plab is - // retired, the difference between 'actual_size' and 'promoted' will be returned to - // the old generation's promotion reserve (i.e., it will be 'unexpended'). - size_t _plab_promoted; - - // If false, no more promotion by this thread during this evacuation phase. - bool _plab_allows_promotion; - - // If true, evacuations may attempt to allocate a smaller plab if the original size fails. - bool _plab_retries_enabled; + ShenandoahPLAB* _shenandoah_plab; ShenandoahEvacuationStats* _evacuation_stats; @@ -141,8 +125,7 @@ public: data(thread)->_gclab_size = 0; if (ShenandoahHeap::heap()->mode()->is_generational()) { - data(thread)->_plab = new PLAB(align_up(PLAB::min_size(), CardTable::card_size_in_words())); - data(thread)->_plab_desired_size = 0; + data(thread)->_shenandoah_plab = new ShenandoahPLAB(); } } @@ -170,65 +153,8 @@ public: return data(thread)->_evacuation_stats; } - static PLAB* plab(Thread* thread) { - return data(thread)->_plab; - } - - static size_t plab_size(Thread* thread) { - return data(thread)->_plab_desired_size; - } - - static void set_plab_size(Thread* thread, size_t v) { - data(thread)->_plab_desired_size = v; - } - - static void enable_plab_retries(Thread* thread) { - data(thread)->_plab_retries_enabled = true; - } - - static void disable_plab_retries(Thread* thread) { - data(thread)->_plab_retries_enabled = false; - } - - static bool plab_retries_enabled(Thread* thread) { - return data(thread)->_plab_retries_enabled; - } - - static void enable_plab_promotions(Thread* thread) { - data(thread)->_plab_allows_promotion = true; - } - - static void disable_plab_promotions(Thread* thread) { - data(thread)->_plab_allows_promotion = false; - } - - static bool allow_plab_promotions(Thread* thread) { - return data(thread)->_plab_allows_promotion; - } - - static void reset_plab_promoted(Thread* thread) { - data(thread)->_plab_promoted = 0; - } - - static void add_to_plab_promoted(Thread* thread, size_t increment) { - data(thread)->_plab_promoted += increment; - } - - static void subtract_from_plab_promoted(Thread* thread, size_t increment) { - assert(data(thread)->_plab_promoted >= increment, "Cannot subtract more than remaining promoted"); - data(thread)->_plab_promoted -= increment; - } - - static size_t get_plab_promoted(Thread* thread) { - return data(thread)->_plab_promoted; - } - - static void set_plab_actual_size(Thread* thread, size_t value) { - data(thread)->_plab_actual_size = value; - } - - static size_t get_plab_actual_size(Thread* thread) { - return data(thread)->_plab_actual_size; + static ShenandoahPLAB* shenandoah_plab(Thread* thread) { + return data(thread)->_shenandoah_plab; } // Evacuation OOM handling diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUncommitThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahUncommitThread.cpp index ec708b198e7..f2fcd39daf5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUncommitThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUncommitThread.cpp @@ -32,7 +32,7 @@ ShenandoahUncommitThread::ShenandoahUncommitThread(ShenandoahHeap* heap) : _heap(heap), _uncommit_lock(Mutex::safepoint - 2, "ShenandoahUncommit_lock", true) { - set_name("Shenandoah Uncommit Thread"); + set_name("ShenUncommit"); create_and_start(); // Allow uncommits. This is managed by the control thread during a GC. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp b/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp index 176baa133c8..dea47fcbf4f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp @@ -40,6 +40,22 @@ ShenandoahPhaseTimings::Phase ShenandoahTimingsTracker::_current_phase = ShenandoahPhaseTimings::_invalid_phase; +const char* ShenandoahGCSession::cycle_end_message(ShenandoahGenerationType type) { + switch (type) { + case NON_GEN: + return "end of GC cycle"; + case GLOBAL: + return "end of Global GC cycle"; + case YOUNG: + return "end of Young GC cycle"; + case OLD: + return "end of Old GC cycle"; + default: + ShouldNotReachHere(); + return "end of GC cycle"; + } +} + ShenandoahGCSession::ShenandoahGCSession(GCCause::Cause cause, ShenandoahGeneration* generation) : _heap(ShenandoahHeap::heap()), _generation(generation), @@ -54,7 +70,7 @@ ShenandoahGCSession::ShenandoahGCSession(GCCause::Cause cause, ShenandoahGenerat _heap->trace_heap_before_gc(_tracer); _trace_cycle.initialize(_heap->cycle_memory_manager(), cause, - "end of GC cycle", + cycle_end_message(_generation->type()), /* allMemoryPoolsAffected */ true, /* recordGCBeginTime = */ true, /* recordPreGCUsage = */ true, diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp index 8a508c4afd8..6ef4cd7c702 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp @@ -67,6 +67,8 @@ private: GCTracer* const _tracer; TraceMemoryManagerStats _trace_cycle; + + static const char* cycle_end_message(ShenandoahGenerationType type); public: ShenandoahGCSession(GCCause::Cause cause, ShenandoahGeneration* generation); ~ShenandoahGCSession(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp index 225339a3219..afef11640c9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp @@ -1086,7 +1086,7 @@ void ShenandoahVerifier::verify_generic(ShenandoahGeneration* generation, Verify _verify_cset_disable, // cset may be inconsistent _verify_liveness_disable, // no reliable liveness data _verify_regions_disable, // no reliable region data - _verify_size_exact, // expect generation and heap sizes to match exactly + _verify_size_disable, // no reliable sizing data _verify_gcstate_disable // no data about gcstate ); } diff --git a/src/hotspot/share/gc/z/zNMT.cpp b/src/hotspot/share/gc/z/zNMT.cpp index 1019bcfdd96..b82afac47bc 100644 --- a/src/hotspot/share/gc/z/zNMT.cpp +++ b/src/hotspot/share/gc/z/zNMT.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -45,9 +45,7 @@ void ZNMT::unreserve(zaddress_unsafe start, size_t size) { if (MemTracker::enabled()) { // We are the owner of the reserved memory, and any failure to unreserve - // are fatal, so so we don't need to hold a lock while unreserving memory. - - MemTracker::NmtVirtualMemoryLocker nvml; + // are fatal, so we don't need to hold a lock while unreserving memory. // The current NMT implementation does not support unreserving a memory // region that was built up from smaller memory reservations. Workaround diff --git a/src/hotspot/share/interpreter/bytecodeTracer.cpp b/src/hotspot/share/interpreter/bytecodeTracer.cpp index 21974218957..2610a8a2bd6 100644 --- a/src/hotspot/share/interpreter/bytecodeTracer.cpp +++ b/src/hotspot/share/interpreter/bytecodeTracer.cpp @@ -78,7 +78,7 @@ class BytecodePrinter { void print_field_or_method(int cp_index, outputStream* st); void print_dynamic(int cp_index, outputStream* st); void print_attributes(int bci, outputStream* st); - void bytecode_epilog(int bci, outputStream* st); + void print_method_data_at(int bci, outputStream* st); public: BytecodePrinter(int flags = 0) : _is_wide(false), _code(Bytecodes::_illegal), _flags(flags) {} @@ -171,7 +171,9 @@ class BytecodePrinter { } _next_pc = is_wide() ? bcp+2 : bcp+1; print_attributes(bci, st); - bytecode_epilog(bci, st); + if (ClassPrinter::has_mode(_flags, ClassPrinter::PRINT_METHOD_DATA)) { + print_method_data_at(bci, st); + } } }; @@ -598,7 +600,7 @@ void BytecodePrinter::print_attributes(int bci, outputStream* st) { } -void BytecodePrinter::bytecode_epilog(int bci, outputStream* st) { +void BytecodePrinter::print_method_data_at(int bci, outputStream* st) { MethodData* mdo = method()->method_data(); if (mdo != nullptr) { diff --git a/src/hotspot/share/interpreter/interpreter.cpp b/src/hotspot/share/interpreter/interpreter.cpp index 2cc163186e8..1f327152e0c 100644 --- a/src/hotspot/share/interpreter/interpreter.cpp +++ b/src/hotspot/share/interpreter/interpreter.cpp @@ -61,10 +61,10 @@ void InterpreterCodelet::initialize(const char* description, Bytecodes::Code byt void InterpreterCodelet::verify() {} -void InterpreterCodelet::print_on(outputStream* st) const { +void InterpreterCodelet::print_on(outputStream* st, bool print_code) const { ttyLocker ttyl; - if (AbstractInterpreter::should_print_instructions()) { + if (print_code) { st->cr(); st->print_cr("----------------------------------------------------------------------"); } @@ -74,12 +74,16 @@ void InterpreterCodelet::print_on(outputStream* st) const { st->print_cr("[" INTPTR_FORMAT ", " INTPTR_FORMAT "] %d bytes", p2i(code_begin()), p2i(code_end()), code_size()); - if (AbstractInterpreter::should_print_instructions()) { + if (print_code) { st->cr(); Disassembler::decode(code_begin(), code_end(), st NOT_PRODUCT(COMMA &_asm_remarks)); } } +void InterpreterCodelet::print_on(outputStream* st) const { + print_on(st, AbstractInterpreter::should_print_instructions()); +} + void InterpreterCodelet::print() const { print_on(tty); } CodeletMark::CodeletMark(InterpreterMacroAssembler*& masm, diff --git a/src/hotspot/share/interpreter/interpreter.hpp b/src/hotspot/share/interpreter/interpreter.hpp index f7d42fcb4da..fb368638332 100644 --- a/src/hotspot/share/interpreter/interpreter.hpp +++ b/src/hotspot/share/interpreter/interpreter.hpp @@ -67,6 +67,7 @@ class InterpreterCodelet: public Stub { // Debugging void verify(); + void print_on(outputStream* st, bool print_code) const; void print_on(outputStream* st) const; void print() const; diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp index e3cf5d589c2..59d5f29023c 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -55,7 +55,6 @@ #include "prims/jvmtiExport.hpp" #include "prims/methodHandles.hpp" #include "prims/nativeLookup.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/continuation.hpp" #include "runtime/deoptimization.hpp" #include "runtime/fieldDescriptor.inline.hpp" @@ -75,6 +74,7 @@ #include "utilities/checkedCast.hpp" #include "utilities/copy.hpp" #include "utilities/events.hpp" +#include "utilities/exceptions.hpp" #if INCLUDE_JFR #include "jfr/jfr.inline.hpp" #endif @@ -352,7 +352,7 @@ JRT_ENTRY(void, InterpreterRuntime::throw_StackOverflowError(JavaThread* current vmClasses::StackOverflowError_klass(), CHECK); // Increment counter for hs_err file reporting - AtomicAccess::inc(&Exceptions::_stack_overflow_errors); + Exceptions::increment_stack_overflow_errors(); // Remove the ScopedValue bindings in case we got a StackOverflowError // while we were trying to manipulate ScopedValue bindings. current->clear_scopedValueBindings(); @@ -366,7 +366,7 @@ JRT_ENTRY(void, InterpreterRuntime::throw_delayed_StackOverflowError(JavaThread* java_lang_Throwable::set_message(exception(), Universe::delayed_stack_overflow_error_message()); // Increment counter for hs_err file reporting - AtomicAccess::inc(&Exceptions::_stack_overflow_errors); + Exceptions::increment_stack_overflow_errors(); // Remove the ScopedValue bindings in case we got a StackOverflowError // while we were trying to manipulate ScopedValue bindings. current->clear_scopedValueBindings(); diff --git a/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp b/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp index 59a9d8a9090..a6e97ab227a 100644 --- a/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp +++ b/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp @@ -228,7 +228,7 @@ void JfrJavaSupport::new_object_global_ref(JfrJavaArguments* args, TRAPS) { jstring JfrJavaSupport::new_string(const char* c_str, TRAPS) { assert(c_str != nullptr, "invariant"); DEBUG_ONLY(check_java_thread_in_vm(THREAD)); - const oop result = java_lang_String::create_oop_from_str(c_str, THREAD); + const oop result = java_lang_String::create_oop_from_str(c_str, CHECK_NULL); return (jstring)local_jni_handle(result, THREAD); } diff --git a/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.cpp b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.cpp index 83eee96091e..8b5819e92c4 100644 --- a/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.cpp +++ b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.cpp @@ -34,6 +34,7 @@ #include "memory/resourceArea.hpp" #include "oops/access.inline.hpp" #include "oops/oop.inline.hpp" +#include "runtime/os.hpp" #include "utilities/align.hpp" UnifiedOopRef DFSClosure::_reference_stack[max_dfs_depth]; @@ -67,9 +68,27 @@ void DFSClosure::find_leaks_from_root_set(EdgeStore* edge_store, rs.process(); } +static address calculate_headroom_limit() { + static constexpr size_t required_headroom = K * 64; + const Thread* const t = Thread::current_or_null(); + return t->stack_end() + required_headroom; +} + DFSClosure::DFSClosure(EdgeStore* edge_store, JFRBitSet* mark_bits, const Edge* start_edge) :_edge_store(edge_store), _mark_bits(mark_bits), _start_edge(start_edge), - _max_depth(max_dfs_depth), _depth(0), _ignore_root_set(false) { + _max_depth(max_dfs_depth), _depth(0), _ignore_root_set(false), + _headroom_limit(calculate_headroom_limit()) { +} + +bool DFSClosure::have_headroom() const { + const address sp = (address) os::current_stack_pointer(); +#ifdef ASSERT + const Thread* const t = Thread::current_or_null(); + assert(t->is_VM_thread(), "invariant"); + assert(t->is_in_full_stack(_headroom_limit), "invariant"); + assert(t->is_in_full_stack(sp), "invariant"); +#endif + return sp > _headroom_limit; } void DFSClosure::closure_impl(UnifiedOopRef reference, const oop pointee) { @@ -97,7 +116,7 @@ void DFSClosure::closure_impl(UnifiedOopRef reference, const oop pointee) { } } assert(_max_depth >= 1, "invariant"); - if (_depth < _max_depth - 1) { + if (_depth < _max_depth - 1 && have_headroom()) { _depth++; pointee->oop_iterate(this); assert(_depth > 0, "invariant"); diff --git a/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp index 98364690422..9ee15ff07e0 100644 --- a/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp +++ b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp @@ -46,12 +46,15 @@ class DFSClosure : public BasicOopIterateClosure { size_t _max_depth; size_t _depth; bool _ignore_root_set; + const address _headroom_limit; DFSClosure(EdgeStore* edge_store, JFRBitSet* mark_bits, const Edge* start_edge); void add_chain(); void closure_impl(UnifiedOopRef reference, const oop pointee); + bool have_headroom() const; + public: virtual ReferenceIterationMode reference_iteration_mode() { return DO_FIELDS_EXCEPT_REFERENT; } diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp index 3ad6a197d07..20d61b542b0 100644 --- a/src/hotspot/share/logging/logTag.hpp +++ b/src/hotspot/share/logging/logTag.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -90,6 +90,7 @@ class outputStream; LOG_TAG(freelist) \ LOG_TAG(gc) \ NOT_PRODUCT(LOG_TAG(generate)) \ + LOG_TAG(generateoopmap) \ LOG_TAG(handshake) \ LOG_TAG(hashtables) \ LOG_TAG(heap) \ diff --git a/src/hotspot/share/nmt/memTracker.hpp b/src/hotspot/share/nmt/memTracker.hpp index d9ebf4dc30e..6b5b6affa14 100644 --- a/src/hotspot/share/nmt/memTracker.hpp +++ b/src/hotspot/share/nmt/memTracker.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, 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 @@ -140,6 +140,7 @@ class MemTracker : AllStatic { assert_post_init(); if (!enabled()) return; if (addr != nullptr) { + NmtVirtualMemoryLocker nvml; VirtualMemoryTracker::Instance::remove_released_region((address)addr, size); } } diff --git a/src/hotspot/share/nmt/threadStackTracker.cpp b/src/hotspot/share/nmt/threadStackTracker.cpp index 3e649d882c4..6fb17c93782 100644 --- a/src/hotspot/share/nmt/threadStackTracker.cpp +++ b/src/hotspot/share/nmt/threadStackTracker.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2019, 2024, Red Hat, Inc. All rights reserved. - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -60,7 +60,6 @@ void ThreadStackTracker::delete_thread_stack(void* base, size_t size) { assert(base != nullptr, "Should have been filtered"); align_thread_stack_boundaries_inward(base, size); - MemTracker::NmtVirtualMemoryLocker nvml; MemTracker::record_virtual_memory_release((address)base, size); _thread_count--; } diff --git a/src/hotspot/share/oops/bsmAttribute.hpp b/src/hotspot/share/oops/bsmAttribute.hpp index a28d2757fb0..32bc58b5b07 100644 --- a/src/hotspot/share/oops/bsmAttribute.hpp +++ b/src/hotspot/share/oops/bsmAttribute.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -61,18 +61,18 @@ public: _argv_offset = 2 }; - int bootstrap_method_index() const { + u2 bootstrap_method_index() const { return _bootstrap_method_index; } - int argument_count() const { + u2 argument_count() const { return _argument_count; } - int argument(int n) const { - assert(checked_cast(n) < _argument_count, "oob"); + u2 argument(u2 n) const { + assert(n < _argument_count, "oob"); return argument_indexes()[n]; } - void set_argument(int index, u2 value) { + void set_argument(u2 index, u2 value) { assert(index >= 0 && index < argument_count(), "invariant"); argument_indexes()[index] = value; } diff --git a/src/hotspot/share/oops/bsmAttribute.inline.hpp b/src/hotspot/share/oops/bsmAttribute.inline.hpp index e678c280c26..8e048704e08 100644 --- a/src/hotspot/share/oops/bsmAttribute.inline.hpp +++ b/src/hotspot/share/oops/bsmAttribute.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -47,7 +47,7 @@ inline BSMAttributeEntry* BSMAttributeEntries::InsertionIterator::reserve_new_en inline void BSMAttributeEntry::copy_args_into(BSMAttributeEntry* entry) const { assert(entry->argument_count() == this->argument_count(), "must be same"); - for (int i = 0; i < argument_count(); i++) { + for (u2 i = 0; i < argument_count(); i++) { entry->set_argument(i, this->argument(i)); } } diff --git a/src/hotspot/share/oops/compressedKlass.hpp b/src/hotspot/share/oops/compressedKlass.hpp index 64b9fcf9c82..98a9eafdbf4 100644 --- a/src/hotspot/share/oops/compressedKlass.hpp +++ b/src/hotspot/share/oops/compressedKlass.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -231,7 +231,7 @@ public: // Returns the alignment a Klass* is guaranteed to have. // Note: *Not* the same as 1 << shift ! Klass are always guaranteed to be at least 64-bit aligned, // so this will return 8 even if shift is 0. - static int klass_alignment_in_bytes() { return nth_bit(MAX2(3, _shift)); } + static int klass_alignment_in_bytes() { return static_cast(nth_bit(MAX2(3, _shift))); } static int klass_alignment_in_words() { return klass_alignment_in_bytes() / BytesPerWord; } // Returns the highest possible narrowKlass value given the current Klass range diff --git a/src/hotspot/share/oops/constantPool.hpp b/src/hotspot/share/oops/constantPool.hpp index 6c519945f4d..b4cff2bbbe6 100644 --- a/src/hotspot/share/oops/constantPool.hpp +++ b/src/hotspot/share/oops/constantPool.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -554,8 +554,8 @@ class ConstantPool : public Metadata { u2 bootstrap_argument_index_at(int cp_index, int j) { int bsmai = bootstrap_methods_attribute_index(cp_index); BSMAttributeEntry* bsme = bsm_attribute_entry(bsmai); - assert((uint)j < (uint)bsme->argument_count(), "oob"); - return bsm_attribute_entry(bsmai)->argument(j); + assert(j < bsme->argument_count(), "oob"); + return bsm_attribute_entry(bsmai)->argument(checked_cast(j)); } // The following methods (name/signature/klass_ref_at, klass_ref_at_noresolve, diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp index 34d7aa10299..edb5f6714c0 100644 --- a/src/hotspot/share/oops/cpCache.cpp +++ b/src/hotspot/share/oops/cpCache.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -568,6 +568,8 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv return false; } + int cp_index = method_entry->constant_pool_index(); + if (!method_entry->is_resolved(Bytecodes::_invokevirtual)) { if (method_entry->method() == nullptr) { rejection_reason = "(method entry is not resolved)"; @@ -577,9 +579,24 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv rejection_reason = "(corresponding stub is generated on demand during method resolution)"; return false; // FIXME: corresponding stub is generated on demand during method resolution (see LinkResolver::resolve_static_call). } - if (method_entry->is_resolved(Bytecodes::_invokehandle) && !CDSConfig::is_dumping_method_handles()) { - rejection_reason = "(not dumping method handles)"; - return false; + if (method_entry->is_resolved(Bytecodes::_invokehandle)) { + if (!CDSConfig::is_dumping_method_handles()) { + rejection_reason = "(not dumping method handles)"; + return false; + } + + Symbol* sig = constant_pool()->uncached_signature_ref_at(cp_index); + Klass* k; + if (!AOTConstantPoolResolver::check_methodtype_signature(constant_pool(), sig, &k, true)) { + // invokehandles that were resolved in the training run should have been filtered in + // AOTConstantPoolResolver::maybe_resolve_fmi_ref so we shouldn't come to here. + // + // If we come here it's because the AOT assembly phase has executed an invokehandle + // that uses an excluded type like jdk.jfr.Event. This should not happen because the + // AOT assembly phase should execute only a very limited set of Java code. + ResourceMark rm; + fatal("AOT assembly phase must not resolve any invokehandles whose signatures include an excluded type"); + } } if (method_entry->method()->is_method_handle_intrinsic() && !CDSConfig::is_dumping_method_handles()) { rejection_reason = "(not dumping intrinsic method handles)"; @@ -587,7 +604,6 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv } } - int cp_index = method_entry->constant_pool_index(); assert(src_cp->tag_at(cp_index).is_method() || src_cp->tag_at(cp_index).is_interface_method(), "sanity"); if (!AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) { diff --git a/src/hotspot/share/oops/fieldInfo.hpp b/src/hotspot/share/oops/fieldInfo.hpp index b6d9c4d34e5..88c982e9d1f 100644 --- a/src/hotspot/share/oops/fieldInfo.hpp +++ b/src/hotspot/share/oops/fieldInfo.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, 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 @@ -302,7 +302,7 @@ class FieldStatus { // boilerplate: u1 _flags; - static constexpr u1 flag_mask(FieldStatusBitPosition pos) { return (u1)1 << (int)pos; } + static constexpr u1 flag_mask(FieldStatusBitPosition pos) { return checked_cast(1 << pos); } bool test_flag(FieldStatusBitPosition pos) { return (_flags & flag_mask(pos)) != 0; } // this performs an atomic update on a live status byte! void update_flag(FieldStatusBitPosition pos, bool z); diff --git a/src/hotspot/share/oops/generateOopMap.cpp b/src/hotspot/share/oops/generateOopMap.cpp index 5e12c57676d..09912aeaf63 100644 --- a/src/hotspot/share/oops/generateOopMap.cpp +++ b/src/hotspot/share/oops/generateOopMap.cpp @@ -400,9 +400,7 @@ void GenerateOopMap::bb_mark_fct(GenerateOopMap *c, int bci, int *data) { if (c->is_bb_header(bci)) return; - if (TraceNewOopMapGeneration) { - tty->print_cr("Basicblock#%d begins at: %d", c->_bb_count, bci); - } + log_debug(generateoopmap)( "Basicblock#%d begins at: %d", c->_bb_count, bci); c->set_bbmark_bit(bci); c->_bb_count++; } @@ -913,13 +911,12 @@ void GenerateOopMap::do_interpretation() // iterated more than once. int i = 0; do { -#ifndef PRODUCT - if (TraceNewOopMapGeneration) { - tty->print("\n\nIteration #%d of do_interpretation loop, method:\n", i); - method()->print_name(tty); - tty->print("\n\n"); + if (log_is_enabled(Trace, generateoopmap)) { + LogStream st(Log(generateoopmap)::trace()); + st.print("Iteration #%d of do_interpretation loop, method:", i); + method()->print_name(&st); + st.print("\n\n"); } -#endif _conflict = false; _monitor_safe = true; // init_state is now called from init_basic_blocks. The length of a @@ -1084,8 +1081,7 @@ void GenerateOopMap::initialize_vars() { void GenerateOopMap::add_to_ref_init_set(int localNo) { - if (TraceNewOopMapGeneration) - tty->print_cr("Added init vars: %d", localNo); + log_debug(generateoopmap)("Added init vars: %d", localNo); // Is it already in the set? if (_init_vars->contains(localNo) ) @@ -1284,13 +1280,13 @@ void GenerateOopMap::report_monitor_mismatch(const char *msg) { void GenerateOopMap::print_states(outputStream *os, CellTypeState* vec, int num) { for (int i = 0; i < num; i++) { - vec[i].print(tty); + vec[i].print(os); } } // Print the state values at the current bytecode. -void GenerateOopMap::print_current_state(outputStream *os, - BytecodeStream *currentBC, +void GenerateOopMap::print_current_state(outputStream* os, + BytecodeStream* currentBC, bool detailed) { if (detailed) { os->print(" %4d vars = ", currentBC->bci()); @@ -1312,6 +1308,7 @@ void GenerateOopMap::print_current_state(outputStream *os, case Bytecodes::_invokestatic: case Bytecodes::_invokedynamic: case Bytecodes::_invokeinterface: { + ResourceMark rm; int idx = currentBC->has_index_u4() ? currentBC->get_index_u4() : currentBC->get_index_u2(); ConstantPool* cp = method()->constants(); int nameAndTypeIdx = cp->name_and_type_ref_index_at(idx, currentBC->code()); @@ -1342,8 +1339,9 @@ void GenerateOopMap::print_current_state(outputStream *os, // Sets the current state to be the state after executing the // current instruction, starting in the current state. void GenerateOopMap::interp1(BytecodeStream *itr) { - if (TraceNewOopMapGeneration) { - print_current_state(tty, itr, TraceNewOopMapGenerationDetailed); + if (log_is_enabled(Trace, generateoopmap)) { + LogStream st(Log(generateoopmap)::trace()); + print_current_state(&st, itr, Verbose); } // Should we report the results? Result is reported *before* the instruction at the current bci is executed. @@ -2026,9 +2024,7 @@ void GenerateOopMap::ret_jump_targets_do(BytecodeStream *bcs, jmpFct_t jmpFct, i DEBUG_ONLY(BasicBlock* target_bb = &jsr_bb[1];) assert(target_bb == get_basic_block_at(target_bci), "wrong calc. of successor basicblock"); bool alive = jsr_bb->is_alive(); - if (TraceNewOopMapGeneration) { - tty->print("pc = %d, ret -> %d alive: %s\n", bci, target_bci, alive ? "true" : "false"); - } + log_debug(generateoopmap)("pc = %d, ret -> %d alive: %s", bci, target_bci, alive ? "true" : "false"); if (alive) jmpFct(this, target_bci, data); } } @@ -2046,6 +2042,7 @@ char* GenerateOopMap::state_vec_to_string(CellTypeState* vec, int len) { return _state_vec_buf; } +#ifndef PRODUCT void GenerateOopMap::print_time() { tty->print_cr ("Accumulated oopmap times:"); tty->print_cr ("---------------------------"); @@ -2053,6 +2050,7 @@ void GenerateOopMap::print_time() { tty->print_cr (" (%3.0f bytecodes per sec) ", (double)GenerateOopMap::_total_byte_count / GenerateOopMap::_total_oopmap_time.seconds()); } +#endif // // ============ Main Entry Point =========== @@ -2062,27 +2060,16 @@ GenerateOopMap::GenerateOopMap(const methodHandle& method) { _method = method; _max_locals=0; _init_vars = nullptr; - -#ifndef PRODUCT - // If we are doing a detailed trace, include the regular trace information. - if (TraceNewOopMapGenerationDetailed) { - TraceNewOopMapGeneration = true; - } -#endif } bool GenerateOopMap::compute_map(Thread* current) { #ifndef PRODUCT - if (TimeOopMap2) { - method()->print_short_name(tty); - tty->print(" "); - } if (TimeOopMap) { _total_byte_count += method()->code_size(); + TraceTime t_all(nullptr, &_total_oopmap_time, TimeOopMap); } #endif - TraceTime t_single("oopmap time", TimeOopMap2); - TraceTime t_all(nullptr, &_total_oopmap_time, TimeOopMap); + TraceTime t_single("oopmap time", TRACETIME_LOG(Debug, generateoopmap)); // Initialize values _got_error = false; @@ -2099,16 +2086,16 @@ bool GenerateOopMap::compute_map(Thread* current) { _did_rewriting = false; _did_relocation = false; - if (TraceNewOopMapGeneration) { - tty->print("Method name: %s\n", method()->name()->as_C_string()); - if (Verbose) { - _method->print_codes(); - tty->print_cr("Exception table:"); - ExceptionTable excps(method()); - for(int i = 0; i < excps.length(); i ++) { - tty->print_cr("[%d - %d] -> %d", - excps.start_pc(i), excps.end_pc(i), excps.handler_pc(i)); - } + if (log_is_enabled(Debug, generateoopmap)) { + ResourceMark rm; + LogStream st(Log(generateoopmap)::debug()); + st.print_cr("Method name: %s\n", method()->name()->as_C_string()); + _method->print_codes_on(&st); + st.print_cr("Exception table:"); + ExceptionTable excps(method()); + for (int i = 0; i < excps.length(); i ++) { + st.print_cr("[%d - %d] -> %d", + excps.start_pc(i), excps.end_pc(i), excps.handler_pc(i)); } } @@ -2175,7 +2162,7 @@ void GenerateOopMap::verify_error(const char *format, ...) { // void GenerateOopMap::report_result() { - if (TraceNewOopMapGeneration) tty->print_cr("Report result pass"); + log_debug(generateoopmap)("Report result pass"); // We now want to report the result of the parse _report_result = true; @@ -2192,7 +2179,7 @@ void GenerateOopMap::report_result() { } void GenerateOopMap::result_for_basicblock(int bci) { - if (TraceNewOopMapGeneration) tty->print_cr("Report result pass for basicblock"); + log_debug(generateoopmap)("Report result pass for basicblock"); // We now want to report the result of the parse _report_result = true; @@ -2200,7 +2187,7 @@ void GenerateOopMap::result_for_basicblock(int bci) { // Find basicblock and report results BasicBlock* bb = get_basic_block_containing(bci); guarantee(bb != nullptr, "no basic block for bci"); - assert(bb->is_reachable(), "getting result from unreachable basicblock"); + assert(bb->is_reachable(), "getting result from unreachable basicblock %d", bci); bb->set_changed(true); interp_bb(bb); } @@ -2212,9 +2199,7 @@ void GenerateOopMap::result_for_basicblock(int bci) { void GenerateOopMap::record_refval_conflict(int varNo) { assert(varNo>=0 && varNo< _max_locals, "index out of range"); - if (TraceOopMapRewrites) { - tty->print("### Conflict detected (local no: %d)\n", varNo); - } + log_trace(generateoopmap)("### Conflict detected (local no: %d)", varNo); if (!_new_var_map) { _new_var_map = NEW_RESOURCE_ARRAY(int, _max_locals); @@ -2253,10 +2238,12 @@ void GenerateOopMap::rewrite_refval_conflicts() // Tracing flag _did_rewriting = true; - if (TraceOopMapRewrites) { - tty->print_cr("ref/value conflict for method %s - bytecodes are getting rewritten", method()->name()->as_C_string()); - method()->print(); - method()->print_codes(); + if (log_is_enabled(Trace, generateoopmap)) { + ResourceMark rm; + LogStream st(Log(generateoopmap)::trace()); + st.print_cr("ref/value conflict for method %s - bytecodes are getting rewritten", method()->name()->as_C_string()); + method()->print_on(&st); + method()->print_codes_on(&st); } assert(_new_var_map!=nullptr, "nothing to rewrite"); @@ -2266,9 +2253,7 @@ void GenerateOopMap::rewrite_refval_conflicts() if (!_got_error) { for (int k = 0; k < _max_locals && !_got_error; k++) { if (_new_var_map[k] != k) { - if (TraceOopMapRewrites) { - tty->print_cr("Rewriting: %d -> %d", k, _new_var_map[k]); - } + log_trace(generateoopmap)("Rewriting: %d -> %d", k, _new_var_map[k]); rewrite_refval_conflict(k, _new_var_map[k]); if (_got_error) return; nof_conflicts++; @@ -2319,22 +2304,16 @@ bool GenerateOopMap::rewrite_refval_conflict_inst(BytecodeStream *itr, int from, int bci = itr->bci(); if (is_aload(itr, &index) && index == from) { - if (TraceOopMapRewrites) { - tty->print_cr("Rewriting aload at bci: %d", bci); - } + log_trace(generateoopmap)("Rewriting aload at bci: %d", bci); return rewrite_load_or_store(itr, Bytecodes::_aload, Bytecodes::_aload_0, to); } if (is_astore(itr, &index) && index == from) { if (!stack_top_holds_ret_addr(bci)) { - if (TraceOopMapRewrites) { - tty->print_cr("Rewriting astore at bci: %d", bci); - } + log_trace(generateoopmap)("Rewriting astore at bci: %d", bci); return rewrite_load_or_store(itr, Bytecodes::_astore, Bytecodes::_astore_0, to); } else { - if (TraceOopMapRewrites) { - tty->print_cr("Suppress rewriting of astore at bci: %d", bci); - } + log_trace(generateoopmap)("Suppress rewriting of astore at bci: %d", bci); } } @@ -2502,9 +2481,7 @@ void GenerateOopMap::compute_ret_adr_at_TOS() { // TDT: should this be is_good_address() ? if (_stack_top > 0 && stack()[_stack_top-1].is_address()) { _ret_adr_tos->append(bcs.bci()); - if (TraceNewOopMapGeneration) { - tty->print_cr("Ret_adr TOS at bci: %d", bcs.bci()); - } + log_debug(generateoopmap)("Ret_adr TOS at bci: %d", bcs.bci()); } interp1(&bcs); } diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 1963327fc78..919afbf3abd 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -23,6 +23,7 @@ */ #include "cds/aotClassInitializer.hpp" +#include "cds/aotLinkedClassBulkLoader.hpp" #include "cds/aotMetaspace.hpp" #include "cds/archiveUtils.hpp" #include "cds/cdsConfig.hpp" @@ -150,6 +151,7 @@ #endif // ndef DTRACE_ENABLED bool InstanceKlass::_finalization_enabled = true; +static int call_class_initializer_counter = 0; // for debugging static inline bool is_class_loader(const Symbol* class_name, const ClassFileParser& parser) { @@ -884,7 +886,9 @@ void InstanceKlass::assert_no_clinit_will_run_for_aot_initialized_class() const #endif #if INCLUDE_CDS -void InstanceKlass::initialize_with_aot_initialized_mirror(TRAPS) { +// early_init -- we are moving this class into the fully_initialized state before the +// JVM is able to execute any bytecodes. See AOTLinkedClassBulkLoader::is_initializing_classes_early(). +void InstanceKlass::initialize_with_aot_initialized_mirror(bool early_init, TRAPS) { assert(has_aot_initialized_mirror(), "must be"); assert(CDSConfig::is_loading_heap(), "must be"); assert(CDSConfig::is_using_aot_linked_classes(), "must be"); @@ -894,15 +898,36 @@ void InstanceKlass::initialize_with_aot_initialized_mirror(TRAPS) { return; } + if (log_is_enabled(Info, aot, init)) { + ResourceMark rm; + log_info(aot, init)("%s (aot-inited%s)", external_name(), early_init ? ", early" : ""); + } + if (is_runtime_setup_required()) { + assert(!early_init, "must not call"); // Need to take the slow path, which will call the runtimeSetup() function instead // of initialize(CHECK); return; } - if (log_is_enabled(Info, aot, init)) { - ResourceMark rm; - log_info(aot, init)("%s (aot-inited)", external_name()); + + LogTarget(Info, class, init) lt; + if (lt.is_enabled()) { + ResourceMark rm(THREAD); + LogStream ls(lt); + ls.print("%d Initializing ", call_class_initializer_counter++); + name()->print_value_on(&ls); + ls.print_cr("(aot-inited) (" PTR_FORMAT ") by thread \"%s\"", + p2i(this), THREAD->name()); + } + + if (early_init) { + precond(AOTLinkedClassBulkLoader::is_initializing_classes_early()); + precond(is_linked()); + precond(init_thread() == nullptr); + set_init_state(fully_initialized); + fence_and_clear_init_lock(); + return; } link_class(CHECK); @@ -1699,8 +1724,6 @@ ArrayKlass* InstanceKlass::array_klass_or_null() { return array_klass_or_null(1); } -static int call_class_initializer_counter = 0; // for debugging - Method* InstanceKlass::class_initializer() const { Method* clinit = find_method( vmSymbols::class_initializer_name(), vmSymbols::void_method_signature()); diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index e370a3b7a7c..dd563ad3492 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -556,7 +556,7 @@ public: // initialization (virtuals from Klass) bool should_be_initialized() const override; // means that initialize should be called - void initialize_with_aot_initialized_mirror(TRAPS); + void initialize_with_aot_initialized_mirror(bool early_init, TRAPS); void assert_no_clinit_will_run_for_aot_initialized_class() const NOT_DEBUG_RETURN; void initialize(TRAPS) override; void initialize_preemptable(TRAPS) override; diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index e04da430ef0..5520e4ae977 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -816,7 +816,7 @@ Node *AddPNode::Ideal(PhaseGVN *phase, bool can_reshape) { offset = phase->MakeConX(t2->get_con() + t12->get_con()); } else { // Else move the constant to the right. ((A+con)+B) into ((A+B)+con) - address = phase->transform(new AddPNode(in(Base),addp->in(Address),in(Offset))); + address = phase->transform(AddPNode::make_with_base(in(Base), addp->in(Address), in(Offset))); offset = addp->in(Offset); } set_req_X(Address, address, phase); @@ -838,11 +838,11 @@ Node *AddPNode::Ideal(PhaseGVN *phase, bool can_reshape) { // Convert: (ptr + (offset+con)) into (ptr+offset)+con. // The idea is to merge array_base+scaled_index groups together, // and only have different constant offsets from the same base. - const Node *add = in(Offset); - if( add->Opcode() == Op_AddX && add->in(1) != add ) { - const Type *t22 = phase->type( add->in(2) ); - if( t22->singleton() && (t22 != Type::TOP) ) { // Right input is an add of a constant? - set_req(Address, phase->transform(new AddPNode(in(Base),in(Address),add->in(1)))); + const Node* add = in(Offset); + if (add->Opcode() == Op_AddX && add->in(1) != add) { + const Type* t22 = phase->type(add->in(2)); + if (t22->singleton() && (t22 != Type::TOP)) { // Right input is an add of a constant? + set_req(Address, phase->transform(AddPNode::make_with_base(in(Base), in(Address), add->in(1)))); set_req_X(Offset, add->in(2), phase); // puts add on igvn worklist if needed return this; // Made progress } diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp index 6128de00efb..793eff8dd5d 100644 --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -221,17 +221,19 @@ public: // So not really an AddNode. Lives here, because people associate it with // an add. class AddPNode : public Node { +private: + AddPNode(Node* base, Node* ptr, Node* off) : Node(nullptr, base, ptr, off) { + init_class_id(Class_AddP); + assert((ptr->bottom_type() == Type::TOP) || + ((base == Compile::current()->top()) == (ptr->bottom_type()->make_ptr()->isa_oopptr() == nullptr)), + "base input only needed for heap addresses"); + } + public: enum { Control, // When is it safe to do this add? Base, // Base oop, for GC purposes Address, // Actually address, derived from base Offset } ; // Offset added to address - AddPNode(Node *base, Node *ptr, Node *off) : Node(nullptr,base,ptr,off) { - init_class_id(Class_AddP); - assert((ptr->bottom_type() == Type::TOP) || - ((base == Compile::current()->top()) == (ptr->bottom_type()->make_ptr()->isa_oopptr() == nullptr)), - "base input only needed for heap addresses"); - } virtual int Opcode() const; virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); @@ -243,6 +245,18 @@ public: // second return value: intptr_t& offset); + static AddPNode* make_with_base(Node* base, Node* ptr, Node* offset) { + return new AddPNode(base, ptr, offset); + } + + static AddPNode* make_with_base(Node* base, Node* offset) { + return make_with_base(base, base, offset); + } + + static AddPNode* make_off_heap(Node* ptr, Node* offset) { + return make_with_base(Compile::current()->top(), ptr, offset); + } + // Collect the AddP offset values into the elements array, giving up // if there are more than length. int unpack_offsets(Node* elements[], int length) const; diff --git a/src/hotspot/share/opto/arraycopynode.cpp b/src/hotspot/share/opto/arraycopynode.cpp index 4ee6107fe54..82d17440c56 100644 --- a/src/hotspot/share/opto/arraycopynode.cpp +++ b/src/hotspot/share/opto/arraycopynode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -226,8 +226,8 @@ Node* ArrayCopyNode::try_clone_instance(PhaseGVN *phase, bool can_reshape, int c ciField* field = ik->nonstatic_field_at(i); const TypePtr* adr_type = phase->C->alias_type(field)->adr_type(); Node* off = phase->MakeConX(field->offset_in_bytes()); - Node* next_src = phase->transform(new AddPNode(base_src,base_src,off)); - Node* next_dest = phase->transform(new AddPNode(base_dest,base_dest,off)); + Node* next_src = phase->transform(AddPNode::make_with_base(base_src, off)); + Node* next_dest = phase->transform(AddPNode::make_with_base(base_dest, off)); assert(phase->C->get_alias_index(adr_type) == phase->C->get_alias_index(phase->type(next_src)->isa_ptr()), "slice of address and input slice don't match"); assert(phase->C->get_alias_index(adr_type) == phase->C->get_alias_index(phase->type(next_dest)->isa_ptr()), @@ -332,11 +332,11 @@ bool ArrayCopyNode::prepare_array_copy(PhaseGVN *phase, bool can_reshape, Node* dest_scale = phase->transform(new LShiftXNode(dest_offset, phase->intcon(shift))); - adr_src = phase->transform(new AddPNode(base_src, base_src, src_scale)); - adr_dest = phase->transform(new AddPNode(base_dest, base_dest, dest_scale)); + adr_src = phase->transform(AddPNode::make_with_base(base_src, src_scale)); + adr_dest = phase->transform(AddPNode::make_with_base(base_dest, dest_scale)); - adr_src = phase->transform(new AddPNode(base_src, adr_src, phase->MakeConX(header))); - adr_dest = phase->transform(new AddPNode(base_dest, adr_dest, phase->MakeConX(header))); + adr_src = phase->transform(AddPNode::make_with_base(base_src, adr_src, phase->MakeConX(header))); + adr_dest = phase->transform(AddPNode::make_with_base(base_dest, adr_dest, phase->MakeConX(header))); copy_type = dest_elem; } else { @@ -355,8 +355,8 @@ bool ArrayCopyNode::prepare_array_copy(PhaseGVN *phase, bool can_reshape, return false; } - adr_src = phase->transform(new AddPNode(base_src, base_src, src_offset)); - adr_dest = phase->transform(new AddPNode(base_dest, base_dest, dest_offset)); + adr_src = phase->transform(AddPNode::make_with_base(base_src, src_offset)); + adr_dest = phase->transform(AddPNode::make_with_base(base_dest, dest_offset)); // The address is offsetted to an aligned address where a raw copy would start. // If the clone copy is decomposed into load-stores - the address is adjusted to @@ -366,8 +366,8 @@ bool ArrayCopyNode::prepare_array_copy(PhaseGVN *phase, bool can_reshape, int diff = arrayOopDesc::base_offset_in_bytes(elem) - offset; assert(diff >= 0, "clone should not start after 1st array element"); if (diff > 0) { - adr_src = phase->transform(new AddPNode(base_src, adr_src, phase->MakeConX(diff))); - adr_dest = phase->transform(new AddPNode(base_dest, adr_dest, phase->MakeConX(diff))); + adr_src = phase->transform(AddPNode::make_with_base(base_src, adr_src, phase->MakeConX(diff))); + adr_dest = phase->transform(AddPNode::make_with_base(base_dest, adr_dest, phase->MakeConX(diff))); } copy_type = elem; value_type = ary_src->elem(); @@ -425,8 +425,14 @@ Node* ArrayCopyNode::array_copy_forward(PhaseGVN *phase, store(bs, phase, forward_ctl, mm, adr_dest, atp_dest, v, value_type, copy_type); for (int i = 1; i < count; i++) { Node* off = phase->MakeConX(type2aelembytes(copy_type) * i); - Node* next_src = phase->transform(new AddPNode(base_src,adr_src,off)); - Node* next_dest = phase->transform(new AddPNode(base_dest,adr_dest,off)); + Node* next_src = phase->transform(AddPNode::make_with_base(base_src,adr_src,off)); + // We may have narrowed the type of next_src right before calling this method but because this runs with + // PhaseIterGVN::_delay_transform true, explicitly update the type of the AddP so it's consistent with its + // base and load() picks the right memory slice. + phase->set_type(next_src, next_src->Value(phase)); + Node* next_dest = phase->transform(AddPNode::make_with_base(base_dest,adr_dest,off)); + // Same as above + phase->set_type(next_dest, next_dest->Value(phase)); v = load(bs, phase, forward_ctl, mm, next_src, atp_src, value_type, copy_type); store(bs, phase, forward_ctl, mm, next_dest, atp_dest, v, value_type, copy_type); } @@ -463,8 +469,13 @@ Node* ArrayCopyNode::array_copy_backward(PhaseGVN *phase, if (count > 0) { for (int i = count-1; i >= 1; i--) { Node* off = phase->MakeConX(type2aelembytes(copy_type) * i); - Node* next_src = phase->transform(new AddPNode(base_src,adr_src,off)); - Node* next_dest = phase->transform(new AddPNode(base_dest,adr_dest,off)); + Node* next_src = phase->transform(AddPNode::make_with_base(base_src,adr_src,off)); + // We may have narrowed the type of next_src right before calling this method but because this runs with + // PhaseIterGVN::_delay_transform true, explicitly update the type of the AddP so it's consistent with its + // base and store() picks the right memory slice. + phase->set_type(next_src, next_src->Value(phase)); + Node* next_dest = phase->transform(AddPNode::make_with_base(base_dest,adr_dest,off)); + phase->set_type(next_dest, next_dest->Value(phase)); Node* v = load(bs, phase, backward_ctl, mm, next_src, atp_src, value_type, copy_type); store(bs, phase, backward_ctl, mm, next_dest, atp_dest, v, value_type, copy_type); } @@ -592,6 +603,17 @@ Node *ArrayCopyNode::Ideal(PhaseGVN *phase, bool can_reshape) { const Type* value_type = nullptr; bool disjoint_bases = false; + Node* src = in(ArrayCopyNode::Src); + Node* dest = in(ArrayCopyNode::Dest); + // EA may have moved an input to a new slice. EA stores the new address types in the ArrayCopy node itself + // (_src_type/_dest_type). phase->type(src) and _src_type or phase->type(dest) and _dest_type may be different + // when this transformation runs if igvn hasn't had a chance to propagate the new types yet. Make sure the new + // types are taken into account so new Load/Store nodes are created on the right slice. + const TypePtr* atp_src = get_address_type(phase, _src_type, src); + const TypePtr* atp_dest = get_address_type(phase, _dest_type, dest); + phase->set_type(src, phase->type(src)->join_speculative(atp_src)); + phase->set_type(dest, phase->type(dest)->join_speculative(atp_dest)); + if (!prepare_array_copy(phase, can_reshape, adr_src, base_src, adr_dest, base_dest, copy_type, value_type, disjoint_bases)) { @@ -600,10 +622,6 @@ Node *ArrayCopyNode::Ideal(PhaseGVN *phase, bool can_reshape) { return nullptr; } - Node* src = in(ArrayCopyNode::Src); - Node* dest = in(ArrayCopyNode::Dest); - const TypePtr* atp_src = get_address_type(phase, _src_type, src); - const TypePtr* atp_dest = get_address_type(phase, _dest_type, dest); Node* in_mem = in(TypeFunc::Memory); if (can_reshape) { diff --git a/src/hotspot/share/opto/buildOopMap.cpp b/src/hotspot/share/opto/buildOopMap.cpp index 675113163e8..e3a94f78d9c 100644 --- a/src/hotspot/share/opto/buildOopMap.cpp +++ b/src/hotspot/share/opto/buildOopMap.cpp @@ -377,6 +377,9 @@ OopMap *OopFlow::build_oop_map( Node *n, int max_reg, PhaseRegAlloc *regalloc, i worklist.push(u); } } + if (m->is_SpillCopy()) { + worklist.push(m->in(1)); + } } } #endif diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 1662f808286..e0833afa29d 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -249,6 +249,9 @@ develop(bool, TraceLoopOpts, false, \ "Trace executed loop optimizations") \ \ + develop(bool, TraceSplitIf, false, \ + "Trace Split-If optimization") \ + \ develop(bool, TraceLoopLimitCheck, false, \ "Trace generation of loop limits checks") \ \ @@ -334,6 +337,15 @@ product(bool, PartialPeelLoop, true, \ "Partial peel (rotate) loops") \ \ + product(uint, LoopPeeling, 1, DIAGNOSTIC, \ + "Control loop peeling optimization: " \ + "0 = always disable loop peeling, " \ + "1 = enable loop peeling (default), " \ + "2 = disable loop peeling as a standalone optimization but " \ + "allow it as a helper to other loop optimizations like removing " \ + "empty loops") \ + range(0, 2) \ + \ product(intx, PartialPeelNewPhiDelta, 0, \ "Additional phis that can be created by partial peeling") \ range(0, max_jint) \ diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index 9b3d7b38d15..e01feb874ef 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -844,6 +844,10 @@ bool CallNode::may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) { } } guarantee(dest != nullptr, "Call had only one ptr in, broken IR!"); + if (phase->type(dest)->isa_rawptr()) { + // may happen for an arraycopy that initializes a newly allocated object. Conservatively return true; + return true; + } if (!dest->is_top() && may_modify_arraycopy_helper(phase->type(dest)->is_oopptr(), t_oop, phase)) { return true; } @@ -1741,8 +1745,8 @@ Node *AllocateNode::make_ideal_mark(PhaseGVN* phase, Node* control, Node* mem) { Node* mark_node = nullptr; if (UseCompactObjectHeaders) { Node* klass_node = in(AllocateNode::KlassNode); - Node* proto_adr = phase->transform(new AddPNode(phase->C->top(), klass_node, phase->MakeConX(in_bytes(Klass::prototype_header_offset())))); - mark_node = LoadNode::make(*phase, control, mem, proto_adr, TypeRawPtr::BOTTOM, TypeX_X, TypeX_X->basic_type(), MemNode::unordered); + Node* proto_adr = phase->transform(AddPNode::make_off_heap(klass_node, phase->MakeConX(in_bytes(Klass::prototype_header_offset())))); + mark_node = LoadNode::make(*phase, control, mem, proto_adr, phase->type(proto_adr)->is_ptr(), TypeX_X, TypeX_X->basic_type(), MemNode::unordered); } else { // For now only enable fast locking for non-array types mark_node = phase->MakeConX(markWord::prototype().value()); diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp index 4e3750b5ee5..54269a56c19 100644 --- a/src/hotspot/share/opto/castnode.cpp +++ b/src/hotspot/share/opto/castnode.cpp @@ -470,9 +470,7 @@ static inline Node* addP_of_X2P(PhaseGVN *phase, if (negate) { dispX = phase->transform(new SubXNode(phase->MakeConX(0), dispX)); } - return new AddPNode(phase->C->top(), - phase->transform(new CastX2PNode(base)), - dispX); + return AddPNode::make_off_heap(phase->transform(new CastX2PNode(base)), dispX); } Node *CastX2PNode::Ideal(PhaseGVN *phase, bool can_reshape) { diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 04061a60bad..a251a253ed1 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -2480,7 +2480,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { } phase->is_IterGVN()->register_new_node_with_optimizer(offset); } - return new AddPNode(base, address, offset); + return AddPNode::make_with_base(base, address, offset); } } } diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index abd93fdd876..719b90ad6dd 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -239,8 +239,10 @@ macro(MemBarRelease) macro(StoreFence) macro(StoreStoreFence) macro(MemBarReleaseLock) +macro(MemBarStoreLoad) macro(MemBarVolatile) macro(MemBarStoreStore) +macro(MemBarFull) macro(MergeMem) macro(MinI) macro(MinL) diff --git a/src/hotspot/share/opto/divnode.cpp b/src/hotspot/share/opto/divnode.cpp index ed72d8a11cf..b398ec27b80 100644 --- a/src/hotspot/share/opto/divnode.cpp +++ b/src/hotspot/share/opto/divnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -419,8 +419,7 @@ static Node *transform_long_divide( PhaseGVN *phase, Node *dividend, jlong divis if (!d_pos) { q = new SubLNode(phase->longcon(0), phase->transform(q)); } - } else if ( !Matcher::use_asm_for_ldiv_by_con(d) ) { // Use hardware DIV instruction when - // it is faster than code generated below. + } else { // Attempt the jlong constant divide -> multiply transform found in // "Division by Invariant Integers using Multiplication" // by Granlund and Montgomery @@ -1592,41 +1591,25 @@ const Type* ModDNode::get_result_if_constant(const Type* dividend, const Type* d return TypeD::make(jdouble_cast(xr)); } -Node* ModFloatingNode::Ideal(PhaseGVN* phase, bool can_reshape) { - if (can_reshape) { - PhaseIterGVN* igvn = phase->is_IterGVN(); - - // Either input is TOP ==> the result is TOP - const Type* dividend_type = phase->type(dividend()); - const Type* divisor_type = phase->type(divisor()); - if (dividend_type == Type::TOP || divisor_type == Type::TOP) { - return phase->C->top(); - } - const Type* constant_result = get_result_if_constant(dividend_type, divisor_type); - if (constant_result != nullptr) { - return make_tuple_of_input_state_and_constant_result(igvn, constant_result); - } +const Type* ModFloatingNode::Value(PhaseGVN* phase) const { + const Type* t = CallLeafPureNode::Value(phase); + if (t == Type::TOP) { return Type::TOP; } + const Type* dividend_type = phase->type(dividend()); + const Type* divisor_type = phase->type(divisor()); + if (dividend_type == Type::TOP || divisor_type == Type::TOP) { + return Type::TOP; } - - return CallLeafPureNode::Ideal(phase, can_reshape); -} - -/* Give a tuple node for ::Ideal to return, made of the input state (control to return addr) - * and the given constant result. Idealization of projections will make sure to transparently - * propagate the input state and replace the result by the said constant. - */ -TupleNode* ModFloatingNode::make_tuple_of_input_state_and_constant_result(PhaseIterGVN* phase, const Type* con) const { - Node* con_node = phase->makecon(con); - TupleNode* tuple = TupleNode::make( - tf()->range(), - in(TypeFunc::Control), - in(TypeFunc::I_O), - in(TypeFunc::Memory), - in(TypeFunc::FramePtr), - in(TypeFunc::ReturnAdr), - con_node); - - return tuple; + const Type* constant_result = get_result_if_constant(dividend_type, divisor_type); + if (constant_result != nullptr) { + const TypeTuple* tt = t->is_tuple(); + uint cnt = tt->cnt(); + uint param_cnt = cnt - TypeFunc::Parms; + const Type** fields = TypeTuple::fields(param_cnt); + fields[TypeFunc::Parms] = constant_result; + if (param_cnt > 1) { fields[TypeFunc::Parms + 1] = Type::HALF; } + return TypeTuple::make(cnt, fields); + } + return t; } //============================================================================= diff --git a/src/hotspot/share/opto/divnode.hpp b/src/hotspot/share/opto/divnode.hpp index 43432b271a4..2598429716f 100644 --- a/src/hotspot/share/opto/divnode.hpp +++ b/src/hotspot/share/opto/divnode.hpp @@ -175,8 +175,6 @@ public: // Base class for float and double modulus class ModFloatingNode : public CallLeafPureNode { - TupleNode* make_tuple_of_input_state_and_constant_result(PhaseIterGVN* phase, const Type* con) const; - protected: virtual Node* dividend() const = 0; virtual Node* divisor() const = 0; @@ -184,7 +182,7 @@ protected: public: ModFloatingNode(Compile* C, const TypeFunc* tf, address addr, const char* name); - Node* Ideal(PhaseGVN* phase, bool can_reshape) override; + const Type* Value(PhaseGVN* phase) const override; }; // Float Modulus diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 5befdd924ff..7041eb0b810 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -780,7 +780,7 @@ Node* ConnectionGraph::split_castpp_load_through_phi(Node* curr_addp, Node* curr base = base->find_out_with(Op_CastPP); } - Node* addr = _igvn->transform(new AddPNode(base, base, curr_addp->in(AddPNode::Offset))); + Node* addr = _igvn->transform(AddPNode::make_with_base(base, curr_addp->in(AddPNode::Offset))); Node* mem = (memory->is_Phi() && (memory->in(0) == region)) ? memory->in(i) : memory; Node* load = curr_load->clone(); load->set_req(0, nullptr); diff --git a/src/hotspot/share/opto/generateOptoStub.cpp b/src/hotspot/share/opto/generateOptoStub.cpp index 77633857cdf..d719c301dc6 100644 --- a/src/hotspot/share/opto/generateOptoStub.cpp +++ b/src/hotspot/share/opto/generateOptoStub.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -88,18 +88,17 @@ void GraphKit::gen_stub(address C_function, const int NoAlias = Compile::AliasIdxBot; - Node* adr_last_Java_pc = basic_plus_adr(top(), - thread, - in_bytes(JavaThread::frame_anchor_offset()) + - in_bytes(JavaFrameAnchor::last_Java_pc_offset())); + Node* adr_last_Java_pc = off_heap_plus_addr(thread, + in_bytes(JavaThread::frame_anchor_offset()) + + in_bytes(JavaFrameAnchor::last_Java_pc_offset())); // Drop in the last_Java_sp. last_Java_fp is not touched. // Always do this after the other "last_Java_frame" fields are set since // as soon as last_Java_sp != nullptr the has_last_Java_frame is true and // users will look at the other fields. // - Node *adr_sp = basic_plus_adr(top(), thread, in_bytes(JavaThread::last_Java_sp_offset())); - Node *last_sp = frameptr(); + Node* adr_sp = off_heap_plus_addr(thread, in_bytes(JavaThread::last_Java_sp_offset())); + Node* last_sp = frameptr(); store_to_memory(control(), adr_sp, last_sp, T_ADDRESS, MemNode::unordered); // Set _thread_in_native @@ -228,7 +227,7 @@ void GraphKit::gen_stub(address C_function, Node* target = map()->in(TypeFunc::Parms); // Runtime call returning oop in TLS? Fetch it out if( pass_tls ) { - Node* adr = basic_plus_adr(top(), thread, in_bytes(JavaThread::vm_result_oop_offset())); + Node* adr = off_heap_plus_addr(thread, in_bytes(JavaThread::vm_result_oop_offset())); Node* vm_result = make_load(nullptr, adr, TypeOopPtr::BOTTOM, T_OBJECT, MemNode::unordered); map()->set_req(TypeFunc::Parms, vm_result); // vm_result passed as result // clear thread-local-storage(tls) @@ -237,7 +236,7 @@ void GraphKit::gen_stub(address C_function, //----------------------------- // check exception - Node* adr = basic_plus_adr(top(), thread, in_bytes(Thread::pending_exception_offset())); + Node* adr = off_heap_plus_addr(thread, in_bytes(Thread::pending_exception_offset())); Node* pending = make_load(nullptr, adr, TypeOopPtr::BOTTOM, T_OBJECT, MemNode::unordered); Node* exit_memory = reset_memory(); diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index c969abb85bb..e297292770e 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -508,7 +508,7 @@ void GraphKit::uncommon_trap_if_should_post_on_exceptions(Deoptimization::DeoptR // first must access the should_post_on_exceptions_flag in this thread's JavaThread Node* jthread = _gvn.transform(new ThreadLocalNode()); - Node* adr = basic_plus_adr(top(), jthread, in_bytes(JavaThread::should_post_on_exceptions_flag_offset())); + Node* adr = off_heap_plus_addr(jthread, in_bytes(JavaThread::should_post_on_exceptions_flag_offset())); Node* should_post_flag = make_load(control(), adr, TypeInt::INT, T_INT, MemNode::unordered); // Test the should_post_on_exceptions_flag vs. 0 @@ -670,6 +670,48 @@ ciInstance* GraphKit::builtin_throw_exception(Deoptimization::DeoptReason reason } } +GraphKit::SavedState::SavedState(GraphKit* kit) : + _kit(kit), + _sp(kit->sp()), + _jvms(kit->jvms()), + _map(kit->clone_map()), + _discarded(false) +{ + for (DUIterator_Fast imax, i = kit->control()->fast_outs(imax); i < imax; i++) { + Node* out = kit->control()->fast_out(i); + if (out->is_CFG()) { + _ctrl_succ.push(out); + } + } +} + +GraphKit::SavedState::~SavedState() { + if (_discarded) { + _kit->destruct_map_clone(_map); + return; + } + _kit->jvms()->set_map(_map); + _kit->jvms()->set_sp(_sp); + _map->set_jvms(_kit->jvms()); + _kit->set_map(_map); + _kit->set_sp(_sp); + for (DUIterator_Fast imax, i = _kit->control()->fast_outs(imax); i < imax; i++) { + Node* out = _kit->control()->fast_out(i); + if (out->is_CFG() && out->in(0) == _kit->control() && out != _kit->map() && !_ctrl_succ.member(out)) { + _kit->_gvn.hash_delete(out); + out->set_req(0, _kit->C->top()); + _kit->C->record_for_igvn(out); + --i; --imax; + _kit->_gvn.hash_find_insert(out); + } + } +} + +void GraphKit::SavedState::discard() { + _discarded = true; +} + + //----------------------------PreserveJVMState--------------------------------- PreserveJVMState::PreserveJVMState(GraphKit* kit, bool clone_map) { DEBUG_ONLY(kit->verify_map()); @@ -1192,7 +1234,7 @@ Node* GraphKit::basic_plus_adr(Node* base, Node* ptr, Node* offset) { "Unexpected zero offset - should have matched MakeConX(0)"); } #endif - return _gvn.transform( new AddPNode(base, ptr, offset) ); + return _gvn.transform(AddPNode::make_with_base(base, ptr, offset)); } Node* GraphKit::ConvI2L(Node* offset) { @@ -1678,13 +1720,22 @@ Node* GraphKit::access_load_at(Node* obj, // containing obj return top(); // Dead path ? } + SavedState old_state(this); C2AccessValuePtr addr(adr, adr_type); C2ParseAccess access(this, decorators | C2_READ_ACCESS, bt, obj, addr); + Node* load; if (access.is_raw()) { - return _barrier_set->BarrierSetC2::load_at(access, val_type); + load = _barrier_set->BarrierSetC2::load_at(access, val_type); } else { - return _barrier_set->load_at(access, val_type); + load = _barrier_set->load_at(access, val_type); } + + // Restore the previous state only if the load got folded to a constant + // and we can discard any barriers that might have been added. + if (load == nullptr || !load->is_Con()) { + old_state.discard(); + } + return load; } Node* GraphKit::access_load(Node* adr, // actual address to load val at @@ -1695,13 +1746,22 @@ Node* GraphKit::access_load(Node* adr, // actual address to load val at return top(); // Dead path ? } + SavedState old_state(this); C2AccessValuePtr addr(adr, adr->bottom_type()->is_ptr()); C2ParseAccess access(this, decorators | C2_READ_ACCESS, bt, nullptr, addr); + Node* load; if (access.is_raw()) { - return _barrier_set->BarrierSetC2::load_at(access, val_type); + load = _barrier_set->BarrierSetC2::load_at(access, val_type); } else { - return _barrier_set->load_at(access, val_type); + load = _barrier_set->load_at(access, val_type); } + + // Restore the previous state only if the load got folded to a constant + // and we can discard any barriers that might have been added. + if (load == nullptr || !load->is_Con()) { + old_state.discard(); + } + return load; } Node* GraphKit::access_atomic_cmpxchg_val_at(Node* obj, @@ -2757,9 +2817,9 @@ Node* Phase::gen_subtype_check(Node* subklass, Node* superklass, Node** ctrl, No // will always succeed. We could leave a dependency behind to ensure this. // First load the super-klass's check-offset - Node *p1 = gvn.transform(new AddPNode(C->top(), superklass, gvn.MakeConX(in_bytes(Klass::super_check_offset_offset())))); + Node* p1 = gvn.transform(AddPNode::make_off_heap(superklass, gvn.MakeConX(in_bytes(Klass::super_check_offset_offset())))); Node* m = C->immutable_memory(); - Node *chk_off = gvn.transform(new LoadINode(nullptr, m, p1, gvn.type(p1)->is_ptr(), TypeInt::INT, MemNode::unordered)); + Node* chk_off = gvn.transform(new LoadINode(nullptr, m, p1, gvn.type(p1)->is_ptr(), TypeInt::INT, MemNode::unordered)); int cacheoff_con = in_bytes(Klass::secondary_super_cache_offset()); const TypeInt* chk_off_t = chk_off->Value(&gvn)->isa_int(); int chk_off_con = (chk_off_t != nullptr && chk_off_t->is_con()) ? chk_off_t->get_con() : cacheoff_con; @@ -2775,11 +2835,11 @@ Node* Phase::gen_subtype_check(Node* subklass, Node* superklass, Node** ctrl, No #ifdef _LP64 chk_off_X = gvn.transform(new ConvI2LNode(chk_off_X)); #endif - Node* p2 = gvn.transform(new AddPNode(C->top(), subklass, chk_off_X)); + Node* p2 = gvn.transform(AddPNode::make_off_heap(subklass, chk_off_X)); // For some types like interfaces the following loadKlass is from a 1-word // cache which is mutable so can't use immutable memory. Other // types load from the super-class display table which is immutable. - Node *kmem = C->immutable_memory(); + Node* kmem = C->immutable_memory(); // secondary_super_cache is not immutable but can be treated as such because: // - no ideal node writes to it in a way that could cause an // incorrect/missed optimization of the following Load. @@ -3016,7 +3076,7 @@ bool GraphKit::seems_never_null(Node* obj, ciProfileData* data, bool& speculatin void GraphKit::guard_klass_being_initialized(Node* klass) { int init_state_off = in_bytes(InstanceKlass::init_state_offset()); - Node* adr = basic_plus_adr(top(), klass, init_state_off); + Node* adr = off_heap_plus_addr(klass, init_state_off); Node* init_state = LoadNode::make(_gvn, nullptr, immutable_memory(), adr, adr->bottom_type()->is_ptr(), TypeInt::BYTE, T_BYTE, MemNode::acquire); @@ -3034,7 +3094,7 @@ void GraphKit::guard_klass_being_initialized(Node* klass) { void GraphKit::guard_init_thread(Node* klass) { int init_thread_off = in_bytes(InstanceKlass::init_thread_offset()); - Node* adr = basic_plus_adr(top(), klass, init_thread_off); + Node* adr = off_heap_plus_addr(klass, init_thread_off); Node* init_thread = LoadNode::make(_gvn, nullptr, immutable_memory(), adr, adr->bottom_type()->is_ptr(), TypePtr::NOTNULL, @@ -3612,7 +3672,7 @@ Node* GraphKit::get_layout_helper(Node* klass_node, jint& constant_value) { } } constant_value = Klass::_lh_neutral_value; // put in a known value - Node* lhp = basic_plus_adr(top(), klass_node, in_bytes(Klass::layout_helper_offset())); + Node* lhp = off_heap_plus_addr(klass_node, in_bytes(Klass::layout_helper_offset())); return make_load(nullptr, lhp, TypeInt::INT, T_INT, MemNode::unordered); } diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index 0537d31ae36..273009ca7ce 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -320,6 +320,13 @@ class GraphKit : public Phase { } Node* basic_plus_adr(Node* base, Node* ptr, Node* offset); + Node* off_heap_plus_addr(Node* ptr, intptr_t offset) { + return basic_plus_adr(top(), ptr, MakeConX(offset)); + } + + Node* off_heap_plus_addr(Node* ptr, Node* offset) { + return basic_plus_adr(top(), ptr, offset); + } // Some convenient shortcuts for common nodes Node* IfTrue(IfNode* iff) { return _gvn.transform(new IfTrueNode(iff)); } @@ -346,7 +353,7 @@ class GraphKit : public Phase { Node* CmpP(Node* l, Node* r) { return _gvn.transform(new CmpPNode(l, r)); } Node* Bool(Node* cmp, BoolTest::mask relop) { return _gvn.transform(new BoolNode(cmp, relop)); } - Node* AddP(Node* b, Node* a, Node* o) { return _gvn.transform(new AddPNode(b, a, o)); } + Node* AddP(Node* b, Node* a, Node* o) { return _gvn.transform(AddPNode::make_with_base(b, a, o)); } // Convert between int and long, and size_t. // (See macros ConvI2X, etc., in type.hpp for ConvI2X, etc.) @@ -876,6 +883,29 @@ class GraphKit : public Phase { Node* box_vector(Node* in, const TypeInstPtr* vbox_type, BasicType elem_bt, int num_elem, bool deoptimize_on_exception = false); Node* unbox_vector(Node* in, const TypeInstPtr* vbox_type, BasicType elem_bt, int num_elem); Node* vector_shift_count(Node* cnt, int shift_op, BasicType bt, int num_elem); + + // Helper class to support reverting to a previous parsing state. + // When an intrinsic makes changes before bailing out, it's necessary to restore the graph + // as it was. See JDK-8359344 for what can happen wrong. It's also not always possible to + // bailout before making changes because the bailing out decision might depend on new nodes + // (their types, for instance). + // + // So, if an intrinsic might cause this situation, one must start by saving the state in a + // SavedState by constructing it, and the state will be restored on destruction. If the + // intrinsic is not bailing out, one need to call discard to prevent restoring the old state. + class SavedState : public StackObj { + GraphKit* _kit; + int _sp; + JVMState* _jvms; + SafePointNode* _map; + Unique_Node_List _ctrl_succ; + bool _discarded; + + public: + SavedState(GraphKit*); + ~SavedState(); + void discard(); + }; }; // Helper class to support building of control flow branches. Upon diff --git a/src/hotspot/share/opto/idealKit.hpp b/src/hotspot/share/opto/idealKit.hpp index 518c3b92136..a2ac9e204ba 100644 --- a/src/hotspot/share/opto/idealKit.hpp +++ b/src/hotspot/share/opto/idealKit.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -200,7 +200,7 @@ class IdealKit: public StackObj { // Raw address should be transformed regardless 'delay_transform' flag // to produce canonical form CastX2P(offset). - Node* AddP(Node *base, Node *ptr, Node *off) { return _gvn.transform(new AddPNode(base, ptr, off)); } + Node* AddP(Node* base, Node* ptr, Node* off) { return _gvn.transform(AddPNode::make_with_base(base, ptr, off)); } Node* CmpP(Node* l, Node* r) { return transform(new CmpPNode(l, r)); } #ifdef _LP64 diff --git a/src/hotspot/share/opto/intrinsicnode.cpp b/src/hotspot/share/opto/intrinsicnode.cpp index c3e003ad8d3..3e235738e0f 100644 --- a/src/hotspot/share/opto/intrinsicnode.cpp +++ b/src/hotspot/share/opto/intrinsicnode.cpp @@ -273,7 +273,7 @@ static const Type* bitshuffle_value(const TypeInteger* src_type, const TypeInteg // result.lo = 0 if (maskcon != -1L) { int bitcount = population_count(static_cast(bt == T_INT ? maskcon & 0xFFFFFFFFL : maskcon)); - hi = right_n_bits_typed(bitcount); + hi = right_n_bits(bitcount); lo = 0L; } else { // preserve originally assigned hi (MAX_INT/LONG) and lo (MIN_INT/LONG) values @@ -376,7 +376,7 @@ static const Type* bitshuffle_value(const TypeInteger* src_type, const TypeInteg // Rule 3: // We can further constrain the upper bound of bit compression if the number of bits // which can be set(one) is less than the maximum number of bits of integral type. - hi = MIN2(right_n_bits_typed(result_bit_width), hi); + hi = MIN2(right_n_bits(result_bit_width), hi); } } else { assert(opc == Op_ExpandBits, ""); diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 3627d06a87a..eed99ceb8bc 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -843,6 +843,22 @@ void LibraryCallKit::set_result(RegionNode* region, PhiNode* value) { assert(value->type()->basic_type() == result()->bottom_type()->basic_type(), "sanity"); } +RegionNode* LibraryCallKit::create_bailout() { + RegionNode* bailout = new RegionNode(1); + record_for_igvn(bailout); + return bailout; +} + +bool LibraryCallKit::check_bailout(RegionNode* bailout) { + if (bailout->req() > 1) { + bailout = _gvn.transform(bailout)->as_Region(); + Node* frame = _gvn.transform(new ParmNode(C->start(), TypeFunc::FramePtr)); + Node* halt = _gvn.transform(new HaltNode(bailout, frame, "unexpected guard failure in intrinsic")); + C->root()->add_req(halt); + } + return stopped(); +} + //------------------------------generate_guard--------------------------- // Helper function for generating guarded fast-slow graph structures. // The given 'test', if true, guards a slow path. If the test fails @@ -951,36 +967,19 @@ void LibraryCallKit::generate_string_range_check(Node* array, Node* offset, Node* count, bool char_count, - bool halt_on_oob) { + RegionNode* region) { if (stopped()) { return; // already stopped } - RegionNode* bailout = new RegionNode(1); - record_for_igvn(bailout); if (char_count) { // Convert char count to byte count count = _gvn.transform(new LShiftINode(count, intcon(1))); } - // Offset and count must not be negative - generate_negative_guard(offset, bailout, nullptr, halt_on_oob); - generate_negative_guard(count, bailout, nullptr, halt_on_oob); + generate_negative_guard(offset, region, nullptr, true); + generate_negative_guard(count, region, nullptr, true); // Offset + count must not exceed length of array - generate_limit_guard(offset, count, load_array_length(array), bailout, halt_on_oob); - - if (bailout->req() > 1) { - if (halt_on_oob) { - bailout = _gvn.transform(bailout)->as_Region(); - Node* frame = _gvn.transform(new ParmNode(C->start(), TypeFunc::FramePtr)); - Node* halt = _gvn.transform(new HaltNode(bailout, frame, "unexpected guard failure in intrinsic")); - C->root()->add_req(halt); - } else { - PreserveJVMState pjvms(this); - set_control(_gvn.transform(bailout)); - uncommon_trap(Deoptimization::Reason_intrinsic, - Deoptimization::Action_maybe_recompile); - } - } + generate_limit_guard(offset, count, load_array_length(array), region, true); } Node* LibraryCallKit::current_thread_helper(Node*& tls_output, ByteSize handle_offset, @@ -990,7 +989,7 @@ Node* LibraryCallKit::current_thread_helper(Node*& tls_output, ByteSize handle_o = TypeOopPtr::make_from_klass(thread_klass)->cast_to_ptr_type(TypePtr::NotNull); Node* thread = _gvn.transform(new ThreadLocalNode()); - Node* p = basic_plus_adr(top()/*!oop*/, thread, in_bytes(handle_offset)); + Node* p = off_heap_plus_addr(thread, in_bytes(handle_offset)); tls_output = thread; Node* thread_obj_handle @@ -1139,10 +1138,6 @@ bool LibraryCallKit::inline_array_equals(StrIntrinsicNode::ArgEnc ae) { //------------------------------inline_countPositives------------------------------ // int java.lang.StringCoding#countPositives0(byte[] ba, int off, int len) bool LibraryCallKit::inline_countPositives() { - if (too_many_traps(Deoptimization::Reason_intrinsic)) { - return false; - } - assert(callee()->signature()->size() == 3, "countPositives has 3 parameters"); // no receiver since it is static method Node* ba = argument(0); @@ -1150,8 +1145,9 @@ bool LibraryCallKit::inline_countPositives() { Node* len = argument(2); ba = must_be_not_null(ba, true); - generate_string_range_check(ba, offset, len, false, true); - if (stopped()) { + RegionNode* bailout = create_bailout(); + generate_string_range_check(ba, offset, len, false, bailout); + if (check_bailout(bailout)) { return true; } @@ -1283,9 +1279,6 @@ bool LibraryCallKit::inline_string_indexOf(StrIntrinsicNode::ArgEnc ae) { //-----------------------------inline_string_indexOfI----------------------- bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) { - if (too_many_traps(Deoptimization::Reason_intrinsic)) { - return false; - } if (!Matcher::match_rule_supported(Op_StrIndexOf)) { return false; } @@ -1307,9 +1300,10 @@ bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) { Node* tgt_start = array_element_address(tgt, intcon(0), T_BYTE); // Range checks - generate_string_range_check(src, src_offset, src_count, ae != StrIntrinsicNode::LL, true); - generate_string_range_check(tgt, intcon(0), tgt_count, ae == StrIntrinsicNode::UU, true); - if (stopped()) { + RegionNode* bailout = create_bailout(); + generate_string_range_check(src, src_offset, src_count, ae != StrIntrinsicNode::LL, bailout); + generate_string_range_check(tgt, intcon(0), tgt_count, ae == StrIntrinsicNode::UU, bailout); + if (check_bailout(bailout)) { return true; } @@ -1404,7 +1398,11 @@ bool LibraryCallKit::inline_string_indexOfChar(StrIntrinsicNode::ArgEnc ae) { Node* src_count = _gvn.transform(new SubINode(max, from_index)); // Range checks - generate_string_range_check(src, src_offset, src_count, ae == StrIntrinsicNode::U, true); + RegionNode* bailout = create_bailout(); + generate_string_range_check(src, src_offset, src_count, ae == StrIntrinsicNode::U, bailout); + if (check_bailout(bailout)) { + return true; + } // Check for int_ch >= 0 Node* int_ch_cmp = _gvn.transform(new CmpINode(int_ch, intcon(0))); @@ -1454,9 +1452,6 @@ bool LibraryCallKit::inline_string_indexOfChar(StrIntrinsicNode::ArgEnc ae) { // void StringLatin1.inflate0(byte[] src, int srcOff, char[] dst, int dstOff, int len) // void StringLatin1.inflate0(byte[] src, int srcOff, byte[] dst, int dstOff, int len) bool LibraryCallKit::inline_string_copy(bool compress) { - if (too_many_traps(Deoptimization::Reason_intrinsic)) { - return false; - } int nargs = 5; // 2 oops, 3 ints assert(callee()->signature()->size() == nargs, "string copy has 5 arguments"); @@ -1495,9 +1490,10 @@ bool LibraryCallKit::inline_string_copy(bool compress) { } // Range checks - generate_string_range_check(src, src_offset, length, convert_src, true); - generate_string_range_check(dst, dst_offset, length, convert_dst, true); - if (stopped()) { + RegionNode* bailout = create_bailout(); + generate_string_range_check(src, src_offset, length, convert_src, bailout); + generate_string_range_check(dst, dst_offset, length, convert_dst, bailout); + if (check_bailout(bailout)) { return true; } @@ -1545,12 +1541,10 @@ bool LibraryCallKit::inline_string_copy(bool compress) { #endif //_LP64 //------------------------inline_string_toBytesU-------------------------- -// public static byte[] StringUTF16.toBytes(char[] value, int off, int len) +// public static byte[] StringUTF16.toBytes0(char[] value, int off, int len) bool LibraryCallKit::inline_string_toBytesU() { - if (too_many_traps(Deoptimization::Reason_intrinsic)) { - return false; - } // Get the arguments. + assert(callee()->signature()->size() == 3, "character array encoder requires 3 arguments"); Node* value = argument(0); Node* offset = argument(1); Node* length = argument(2); @@ -1558,30 +1552,18 @@ bool LibraryCallKit::inline_string_toBytesU() { Node* newcopy = nullptr; // Set the original stack and the reexecute bit for the interpreter to reexecute - // the bytecode that invokes StringUTF16.toBytes() if deoptimization happens. + // the bytecode that invokes StringUTF16.toBytes0() if deoptimization happens. { PreserveReexecuteState preexecs(this); jvms()->set_should_reexecute(true); - // Check if a null path was taken unconditionally. - value = null_check(value); - - RegionNode* bailout = new RegionNode(1); - record_for_igvn(bailout); - - // Range checks - generate_negative_guard(offset, bailout); - generate_negative_guard(length, bailout); - generate_limit_guard(offset, length, load_array_length(value), bailout); + value = must_be_not_null(value, true); + RegionNode* bailout = create_bailout(); + generate_negative_guard(offset, bailout, nullptr, true); + generate_negative_guard(length, bailout, nullptr, true); + generate_limit_guard(offset, length, load_array_length(value), bailout, true); // Make sure that resulting byte[] length does not overflow Integer.MAX_VALUE - generate_limit_guard(length, intcon(0), intcon(max_jint/2), bailout); - - if (bailout->req() > 1) { - PreserveJVMState pjvms(this); - set_control(_gvn.transform(bailout)); - uncommon_trap(Deoptimization::Reason_intrinsic, - Deoptimization::Action_maybe_recompile); - } - if (stopped()) { + generate_limit_guard(length, intcon(0), intcon(max_jint/2), bailout, true); + if (check_bailout(bailout)) { return true; } @@ -1640,12 +1622,9 @@ bool LibraryCallKit::inline_string_toBytesU() { } //------------------------inline_string_getCharsU-------------------------- -// public void StringUTF16.getChars(byte[] src, int srcBegin, int srcEnd, char dst[], int dstBegin) +// public void StringUTF16.getChars0(byte[] src, int srcBegin, int srcEnd, char dst[], int dstBegin) bool LibraryCallKit::inline_string_getCharsU() { - if (too_many_traps(Deoptimization::Reason_intrinsic)) { - return false; - } - + assert(callee()->signature()->size() == 5, "StringUTF16.getChars0() has 5 arguments"); // Get the arguments. Node* src = argument(0); Node* src_begin = argument(1); @@ -1658,8 +1637,8 @@ bool LibraryCallKit::inline_string_getCharsU() { AllocateArrayNode* alloc = tightly_coupled_allocation(dst); // Check if a null path was taken unconditionally. - src = null_check(src); - dst = null_check(dst); + src = must_be_not_null(src, true); + dst = must_be_not_null(dst, true); if (stopped()) { return true; } @@ -1669,51 +1648,50 @@ bool LibraryCallKit::inline_string_getCharsU() { src_begin = _gvn.transform(new LShiftINode(src_begin, intcon(1))); // Range checks - generate_string_range_check(src, src_begin, length, true); - generate_string_range_check(dst, dst_begin, length, false); - if (stopped()) { + RegionNode* bailout = create_bailout(); + generate_string_range_check(src, src_begin, length, true, bailout); + generate_string_range_check(dst, dst_begin, length, false, bailout); + if (check_bailout(bailout)) { return true; } - if (!stopped()) { - // Calculate starting addresses. - Node* src_start = array_element_address(src, src_begin, T_BYTE); - Node* dst_start = array_element_address(dst, dst_begin, T_CHAR); + // Calculate starting addresses. + Node* src_start = array_element_address(src, src_begin, T_BYTE); + Node* dst_start = array_element_address(dst, dst_begin, T_CHAR); - // Check if array addresses are aligned to HeapWordSize - const TypeInt* tsrc = gvn().type(src_begin)->is_int(); - const TypeInt* tdst = gvn().type(dst_begin)->is_int(); - bool aligned = tsrc->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_BYTE) + tsrc->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0) && - tdst->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_CHAR) + tdst->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0); + // Check if array addresses are aligned to HeapWordSize + const TypeInt* tsrc = gvn().type(src_begin)->is_int(); + const TypeInt* tdst = gvn().type(dst_begin)->is_int(); + bool aligned = tsrc->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_BYTE) + tsrc->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0) && + tdst->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_CHAR) + tdst->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0); - // Figure out which arraycopy runtime method to call (disjoint, uninitialized). - const char* copyfunc_name = "arraycopy"; - address copyfunc_addr = StubRoutines::select_arraycopy_function(T_CHAR, aligned, true, copyfunc_name, true); - Node* call = make_runtime_call(RC_LEAF|RC_NO_FP, - OptoRuntime::fast_arraycopy_Type(), - copyfunc_addr, copyfunc_name, TypeRawPtr::BOTTOM, - src_start, dst_start, ConvI2X(length) XTOP); - // Do not let reads from the cloned object float above the arraycopy. - if (alloc != nullptr) { - if (alloc->maybe_set_complete(&_gvn)) { - // "You break it, you buy it." - InitializeNode* init = alloc->initialization(); - assert(init->is_complete(), "we just did this"); - init->set_complete_with_arraycopy(); - assert(dst->is_CheckCastPP(), "sanity"); - assert(dst->in(0)->in(0) == init, "dest pinned"); - } - // Do not let stores that initialize this object be reordered with - // a subsequent store that would make this object accessible by - // other threads. - // Record what AllocateNode this StoreStore protects so that - // escape analysis can go from the MemBarStoreStoreNode to the - // AllocateNode and eliminate the MemBarStoreStoreNode if possible - // based on the escape status of the AllocateNode. - insert_mem_bar(Op_MemBarStoreStore, alloc->proj_out_or_null(AllocateNode::RawAddress)); - } else { - insert_mem_bar(Op_MemBarCPUOrder); + // Figure out which arraycopy runtime method to call (disjoint, uninitialized). + const char* copyfunc_name = "arraycopy"; + address copyfunc_addr = StubRoutines::select_arraycopy_function(T_CHAR, aligned, true, copyfunc_name, true); + Node* call = make_runtime_call(RC_LEAF|RC_NO_FP, + OptoRuntime::fast_arraycopy_Type(), + copyfunc_addr, copyfunc_name, TypeRawPtr::BOTTOM, + src_start, dst_start, ConvI2X(length) XTOP); + // Do not let reads from the cloned object float above the arraycopy. + if (alloc != nullptr) { + if (alloc->maybe_set_complete(&_gvn)) { + // "You break it, you buy it." + InitializeNode* init = alloc->initialization(); + assert(init->is_complete(), "we just did this"); + init->set_complete_with_arraycopy(); + assert(dst->is_CheckCastPP(), "sanity"); + assert(dst->in(0)->in(0) == init, "dest pinned"); } + // Do not let stores that initialize this object be reordered with + // a subsequent store that would make this object accessible by + // other threads. + // Record what AllocateNode this StoreStore protects so that + // escape analysis can go from the MemBarStoreStoreNode to the + // AllocateNode and eliminate the MemBarStoreStoreNode if possible + // based on the escape status of the AllocateNode. + insert_mem_bar(Op_MemBarStoreStore, alloc->proj_out_or_null(AllocateNode::RawAddress)); + } else { + insert_mem_bar(Op_MemBarCPUOrder); } C->set_has_split_ifs(true); // Has chance for split-if optimization @@ -1725,9 +1703,16 @@ bool LibraryCallKit::inline_string_getCharsU() { // static void StringUTF16.putChar(byte[] val, int index, int c) // static char StringUTF16.getChar(byte[] val, int index) bool LibraryCallKit::inline_string_char_access(bool is_store) { + Node* ch; + if (is_store) { + assert(callee()->signature()->size() == 3, "StringUTF16.putChar() has 3 arguments"); + ch = argument(2); + } else { + assert(callee()->signature()->size() == 2, "StringUTF16.getChar() has 2 arguments"); + ch = nullptr; + } Node* value = argument(0); Node* index = argument(1); - Node* ch = is_store ? argument(2) : nullptr; // This intrinsic accesses byte[] array as char[] array. Computing the offsets // correctly requires matched array shapes. @@ -2176,7 +2161,7 @@ Node* LibraryCallKit::make_unsafe_address(Node*& base, Node* offset, BasicType t Node* uncasted_base = base; int kind = classify_unsafe_addr(uncasted_base, offset, type); if (kind == Type::RawPtr) { - return basic_plus_adr(top(), uncasted_base, offset); + return off_heap_plus_addr(uncasted_base, offset); } else if (kind == Type::AnyPtr) { assert(base == uncasted_base, "unexpected base change"); if (can_cast) { @@ -2196,13 +2181,13 @@ Node* LibraryCallKit::make_unsafe_address(Node*& base, Node* offset, BasicType t base = null_assert(base); Node* raw_base = _gvn.transform(new CastX2PNode(offset)); offset = MakeConX(0); - return basic_plus_adr(top(), raw_base, offset); + return off_heap_plus_addr(raw_base, offset); } } // We don't know if it's an on heap or off heap access. Fall back // to raw memory access. Node* raw = _gvn.transform(new CheckCastPPNode(control(), base, TypeRawPtr::BOTTOM)); - return basic_plus_adr(top(), raw, offset); + return off_heap_plus_addr(raw, offset); } else { assert(base == uncasted_base, "unexpected base change"); // We know it's an on heap access so base can't be null @@ -2393,47 +2378,6 @@ DecoratorSet LibraryCallKit::mo_decorator_for_access_kind(AccessKind kind) { } } -LibraryCallKit::SavedState::SavedState(LibraryCallKit* kit) : - _kit(kit), - _sp(kit->sp()), - _jvms(kit->jvms()), - _map(kit->clone_map()), - _discarded(false) -{ - for (DUIterator_Fast imax, i = kit->control()->fast_outs(imax); i < imax; i++) { - Node* out = kit->control()->fast_out(i); - if (out->is_CFG()) { - _ctrl_succ.push(out); - } - } -} - -LibraryCallKit::SavedState::~SavedState() { - if (_discarded) { - _kit->destruct_map_clone(_map); - return; - } - _kit->jvms()->set_map(_map); - _kit->jvms()->set_sp(_sp); - _map->set_jvms(_kit->jvms()); - _kit->set_map(_map); - _kit->set_sp(_sp); - for (DUIterator_Fast imax, i = _kit->control()->fast_outs(imax); i < imax; i++) { - Node* out = _kit->control()->fast_out(i); - if (out->is_CFG() && out->in(0) == _kit->control() && out != _kit->map() && !_ctrl_succ.member(out)) { - _kit->_gvn.hash_delete(out); - out->set_req(0, _kit->C->top()); - _kit->C->record_for_igvn(out); - --i; --imax; - _kit->_gvn.hash_find_insert(out); - } - } -} - -void LibraryCallKit::SavedState::discard() { - _discarded = true; -} - bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, const AccessKind kind, const bool unaligned) { if (callee()->is_static()) return false; // caller must have the capability! DecoratorSet decorators = C2_UNSAFE_ACCESS; @@ -2900,7 +2844,7 @@ bool LibraryCallKit::inline_unsafe_fence(vmIntrinsics::ID id) { insert_mem_bar(Op_StoreStoreFence); return true; case vmIntrinsics::_fullFence: - insert_mem_bar(Op_MemBarVolatile); + insert_mem_bar(Op_MemBarFull); return true; default: fatal_unexpected_iid(id); @@ -3018,7 +2962,7 @@ bool LibraryCallKit::inline_unsafe_allocate() { // Note: The argument might still be an illegal value like // Serializable.class or Object[].class. The runtime will handle it. // But we must make an explicit check for initialization. - Node* insp = basic_plus_adr(top(), kls, in_bytes(InstanceKlass::init_state_offset())); + Node* insp = off_heap_plus_addr(kls, in_bytes(InstanceKlass::init_state_offset())); // Use T_BOOLEAN for InstanceKlass::_init_state so the compiler // can generate code to load it as unsigned byte. Node* inst = make_load(nullptr, insp, TypeInt::UBYTE, T_BOOLEAN, MemNode::acquire); @@ -3062,26 +3006,27 @@ bool LibraryCallKit::inline_native_time_funcs(address funcAddr, const char* func // slow path: runtime call // } bool LibraryCallKit::inline_native_vthread_start_transition(address funcAddr, const char* funcName, bool is_final_transition) { - Node* vt_oop = _gvn.transform(must_be_not_null(argument(0), true)); // VirtualThread this argument + Node* vt_oop = must_be_not_null(argument(0), true); // VirtualThread this argument IdealKit ideal(this); Node* thread = ideal.thread(); - Node* jt_addr = basic_plus_adr(top(), thread, in_bytes(JavaThread::is_in_vthread_transition_offset())); + Node* jt_addr = off_heap_plus_addr(thread, in_bytes(JavaThread::is_in_vthread_transition_offset())); Node* vt_addr = basic_plus_adr(vt_oop, java_lang_Thread::is_in_vthread_transition_offset()); access_store_at(nullptr, jt_addr, _gvn.type(jt_addr)->is_ptr(), ideal.ConI(1), TypeInt::BOOL, T_BOOLEAN, IN_NATIVE | MO_UNORDERED); access_store_at(nullptr, vt_addr, _gvn.type(vt_addr)->is_ptr(), ideal.ConI(1), TypeInt::BOOL, T_BOOLEAN, IN_NATIVE | MO_UNORDERED); - insert_mem_bar(Op_MemBarVolatile); + insert_mem_bar(Op_MemBarStoreLoad); ideal.sync_kit(this); Node* global_disable_addr = makecon(TypeRawPtr::make((address)MountUnmountDisabler::global_vthread_transition_disable_count_address())); Node* global_disable = ideal.load(ideal.ctrl(), global_disable_addr, TypeInt::INT, T_INT, Compile::AliasIdxRaw, true /*require_atomic_access*/); Node* vt_disable_addr = basic_plus_adr(vt_oop, java_lang_Thread::vthread_transition_disable_count_offset()); - Node* vt_disable = ideal.load(ideal.ctrl(), vt_disable_addr, TypeInt::INT, T_INT, Compile::AliasIdxRaw, true /*require_atomic_access*/); + const TypePtr* vt_disable_addr_t = _gvn.type(vt_disable_addr)->is_ptr(); + Node* vt_disable = ideal.load(ideal.ctrl(), vt_disable_addr, TypeInt::INT, T_INT, C->get_alias_index(vt_disable_addr_t), true /*require_atomic_access*/); Node* disabled = _gvn.transform(new AddINode(global_disable, vt_disable)); ideal.if_then(disabled, BoolTest::ne, ideal.ConI(0)); { sync_kit(ideal); - Node* is_mount = is_final_transition ? ideal.ConI(0) : _gvn.transform(argument(1)); + Node* is_mount = is_final_transition ? ideal.ConI(0) : argument(1); const TypeFunc* tf = OptoRuntime::vthread_transition_Type(); make_runtime_call(RC_NO_LEAF, tf, funcAddr, funcName, TypePtr::BOTTOM, vt_oop, is_mount); ideal.sync_kit(this); @@ -3093,7 +3038,7 @@ bool LibraryCallKit::inline_native_vthread_start_transition(address funcAddr, co } bool LibraryCallKit::inline_native_vthread_end_transition(address funcAddr, const char* funcName, bool is_first_transition) { - Node* vt_oop = _gvn.transform(must_be_not_null(argument(0), true)); // VirtualThread this argument + Node* vt_oop = must_be_not_null(argument(0), true); // VirtualThread this argument IdealKit ideal(this); Node* _notify_jvmti_addr = makecon(TypeRawPtr::make((address)MountUnmountDisabler::notify_jvmti_events_address())); @@ -3101,13 +3046,13 @@ bool LibraryCallKit::inline_native_vthread_end_transition(address funcAddr, cons ideal.if_then(_notify_jvmti, BoolTest::eq, ideal.ConI(1)); { sync_kit(ideal); - Node* is_mount = is_first_transition ? ideal.ConI(1) : _gvn.transform(argument(1)); + Node* is_mount = is_first_transition ? ideal.ConI(1) : argument(1); const TypeFunc* tf = OptoRuntime::vthread_transition_Type(); make_runtime_call(RC_NO_LEAF, tf, funcAddr, funcName, TypePtr::BOTTOM, vt_oop, is_mount); ideal.sync_kit(this); } ideal.else_(); { Node* thread = ideal.thread(); - Node* jt_addr = basic_plus_adr(top(), thread, in_bytes(JavaThread::is_in_vthread_transition_offset())); + Node* jt_addr = off_heap_plus_addr(thread, in_bytes(JavaThread::is_in_vthread_transition_offset())); Node* vt_addr = basic_plus_adr(vt_oop, java_lang_Thread::is_in_vthread_transition_offset()); sync_kit(ideal); @@ -3132,8 +3077,8 @@ bool LibraryCallKit::inline_native_notify_jvmti_sync() { { // unconditionally update the is_disable_suspend bit in current JavaThread Node* thread = ideal.thread(); - Node* arg = _gvn.transform(argument(0)); // argument for notification - Node* addr = basic_plus_adr(top(), thread, in_bytes(JavaThread::is_disable_suspend_offset())); + Node* arg = argument(0); // argument for notification + Node* addr = off_heap_plus_addr(thread, in_bytes(JavaThread::is_disable_suspend_offset())); const TypePtr *addr_type = _gvn.type(addr)->isa_ptr(); sync_kit(ideal); @@ -3212,7 +3157,7 @@ bool LibraryCallKit::inline_native_classID() { ideal.set(result, _gvn.transform(new AddLNode(array_kls_trace_id, longcon(1)))); } __ else_(); { // void class case - ideal.set(result, _gvn.transform(longcon(LAST_TYPE_ID + 1))); + ideal.set(result, longcon(LAST_TYPE_ID + 1)); } __ end_if(); Node* signaled_flag_address = makecon(TypeRawPtr::make(JfrIntrinsicSupport::signal_address())); @@ -3240,12 +3185,12 @@ bool LibraryCallKit::inline_native_jvm_commit() { // TLS. Node* tls_ptr = _gvn.transform(new ThreadLocalNode()); // Jfr java buffer. - Node* java_buffer_offset = _gvn.transform(new AddPNode(top(), tls_ptr, _gvn.transform(MakeConX(in_bytes(JAVA_BUFFER_OFFSET_JFR))))); + Node* java_buffer_offset = _gvn.transform(AddPNode::make_off_heap(tls_ptr, MakeConX(in_bytes(JAVA_BUFFER_OFFSET_JFR)))); Node* java_buffer = _gvn.transform(new LoadPNode(control(), input_memory_state, java_buffer_offset, TypePtr::BOTTOM, TypeRawPtr::NOTNULL, MemNode::unordered)); - Node* java_buffer_pos_offset = _gvn.transform(new AddPNode(top(), java_buffer, _gvn.transform(MakeConX(in_bytes(JFR_BUFFER_POS_OFFSET))))); + Node* java_buffer_pos_offset = _gvn.transform(AddPNode::make_off_heap(java_buffer, MakeConX(in_bytes(JFR_BUFFER_POS_OFFSET)))); // Load the current value of the notified field in the JfrThreadLocal. - Node* notified_offset = basic_plus_adr(top(), tls_ptr, in_bytes(NOTIFY_OFFSET_JFR)); + Node* notified_offset = off_heap_plus_addr(tls_ptr, in_bytes(NOTIFY_OFFSET_JFR)); Node* notified = make_load(control(), notified_offset, TypeInt::BOOL, T_BOOLEAN, MemNode::unordered); // Test for notification. @@ -3264,7 +3209,7 @@ bool LibraryCallKit::inline_native_jvm_commit() { // Iff notified, the return address of the commit method is the current position of the backing java buffer. This is used to reset the event writer. Node* current_pos_X = _gvn.transform(new LoadXNode(control(), input_memory_state, java_buffer_pos_offset, TypeRawPtr::NOTNULL, TypeX_X, MemNode::unordered)); // Convert the machine-word to a long. - Node* current_pos = _gvn.transform(ConvX2L(current_pos_X)); + Node* current_pos = ConvX2L(current_pos_X); // False branch, not notified. Node* not_notified = _gvn.transform(new IfFalseNode(iff_notified)); @@ -3274,7 +3219,7 @@ bool LibraryCallKit::inline_native_jvm_commit() { // Arg is the next position as a long. Node* arg = argument(0); // Convert long to machine-word. - Node* next_pos_X = _gvn.transform(ConvL2X(arg)); + Node* next_pos_X = ConvL2X(arg); // Store the next_position to the underlying jfr java buffer. store_to_memory(control(), java_buffer_pos_offset, next_pos_X, LP64_ONLY(T_LONG) NOT_LP64(T_INT), MemNode::release); @@ -3283,9 +3228,9 @@ bool LibraryCallKit::inline_native_jvm_commit() { set_all_memory(commit_memory); // Now load the flags from off the java buffer and decide if the buffer is a lease. If so, it needs to be returned post-commit. - Node* java_buffer_flags_offset = _gvn.transform(new AddPNode(top(), java_buffer, _gvn.transform(MakeConX(in_bytes(JFR_BUFFER_FLAGS_OFFSET))))); + Node* java_buffer_flags_offset = _gvn.transform(AddPNode::make_off_heap(java_buffer, MakeConX(in_bytes(JFR_BUFFER_FLAGS_OFFSET)))); Node* flags = make_load(control(), java_buffer_flags_offset, TypeInt::UBYTE, T_BYTE, MemNode::unordered); - Node* lease_constant = _gvn.transform(_gvn.intcon(4)); + Node* lease_constant = _gvn.intcon(4); // And flags with lease constant. Node* lease = _gvn.transform(new AndINode(flags, lease_constant)); @@ -3322,7 +3267,7 @@ bool LibraryCallKit::inline_native_jvm_commit() { lease_compare_rgn->init_req(_true_path, call_return_lease_control); lease_compare_rgn->init_req(_false_path, not_lease); - lease_compare_mem->init_req(_true_path, _gvn.transform(reset_memory())); + lease_compare_mem->init_req(_true_path, reset_memory()); lease_compare_mem->init_req(_false_path, commit_memory); lease_compare_io->init_req(_true_path, i_o()); @@ -3415,7 +3360,7 @@ bool LibraryCallKit::inline_native_getEventWriter() { Node* tls_ptr = _gvn.transform(new ThreadLocalNode()); // Load the address of java event writer jobject handle from the jfr_thread_local structure. - Node* jobj_ptr = basic_plus_adr(top(), tls_ptr, in_bytes(THREAD_LOCAL_WRITER_OFFSET_JFR)); + Node* jobj_ptr = off_heap_plus_addr(tls_ptr, in_bytes(THREAD_LOCAL_WRITER_OFFSET_JFR)); // Load the eventwriter jobject handle. Node* jobj = make_load(control(), jobj_ptr, TypeRawPtr::BOTTOM, T_ADDRESS, MemNode::unordered); @@ -3480,10 +3425,10 @@ bool LibraryCallKit::inline_native_getEventWriter() { IN_HEAP | MO_UNORDERED | C2_MISMATCHED | C2_CONTROL_DEPENDENT_LOAD); // Mask off the excluded information from the epoch. - Node * vthread_is_excluded = _gvn.transform(new AndINode(vthread_epoch_raw, _gvn.transform(excluded_mask))); + Node * vthread_is_excluded = _gvn.transform(new AndINode(vthread_epoch_raw, excluded_mask)); // Branch on excluded to conditionalize updating the epoch for the virtual thread. - Node* is_excluded_cmp = _gvn.transform(new CmpINode(vthread_is_excluded, _gvn.transform(excluded_mask))); + Node* is_excluded_cmp = _gvn.transform(new CmpINode(vthread_is_excluded, excluded_mask)); Node* test_not_excluded = _gvn.transform(new BoolNode(is_excluded_cmp, BoolTest::ne)); IfNode* iff_not_excluded = create_and_map_if(control(), test_not_excluded, PROB_MAX, COUNT_UNKNOWN); @@ -3495,7 +3440,7 @@ bool LibraryCallKit::inline_native_getEventWriter() { set_control(included); // Get epoch value. - Node* epoch = _gvn.transform(new AndINode(vthread_epoch_raw, _gvn.transform(epoch_mask))); + Node* epoch = _gvn.transform(new AndINode(vthread_epoch_raw, epoch_mask)); // Load the current epoch generation. The value is unsigned 16-bit, so we type it as T_CHAR. Node* epoch_generation_address = makecon(TypeRawPtr::make(JfrIntrinsicSupport::epoch_generation_address())); @@ -3533,7 +3478,7 @@ bool LibraryCallKit::inline_native_getEventWriter() { // Update control and phi nodes. epoch_compare_rgn->init_req(_true_path, call_write_checkpoint_control); epoch_compare_rgn->init_req(_false_path, epoch_is_equal); - epoch_compare_mem->init_req(_true_path, _gvn.transform(reset_memory())); + epoch_compare_mem->init_req(_true_path, reset_memory()); epoch_compare_mem->init_req(_false_path, input_memory_state); epoch_compare_io->init_req(_true_path, i_o()); epoch_compare_io->init_req(_false_path, input_io_state); @@ -3574,11 +3519,11 @@ bool LibraryCallKit::inline_native_getEventWriter() { vthread_compare_mem->init_req(_false_path, input_memory_state); vthread_compare_io->init_req(_true_path, _gvn.transform(exclude_compare_io)); vthread_compare_io->init_req(_false_path, input_io_state); - tid->init_req(_true_path, _gvn.transform(vthread_tid)); - tid->init_req(_false_path, _gvn.transform(thread_obj_tid)); - exclusion->init_req(_true_path, _gvn.transform(vthread_is_excluded)); - exclusion->init_req(_false_path, _gvn.transform(threadObj_is_excluded)); - pinVirtualThread->init_req(_true_path, _gvn.transform(continuation_support)); + tid->init_req(_true_path, vthread_tid); + tid->init_req(_false_path, thread_obj_tid); + exclusion->init_req(_true_path, vthread_is_excluded); + exclusion->init_req(_false_path, threadObj_is_excluded); + pinVirtualThread->init_req(_true_path, continuation_support); pinVirtualThread->init_req(_false_path, _gvn.intcon(0)); // Update branch state. @@ -3592,7 +3537,7 @@ bool LibraryCallKit::inline_native_getEventWriter() { ciInstanceKlass* const instklass_EventWriter = klass_EventWriter->as_instance_klass(); const TypeKlassPtr* const aklass = TypeKlassPtr::make(instklass_EventWriter); const TypeOopPtr* const xtype = aklass->as_instance_type(); - Node* jobj_untagged = _gvn.transform(new AddPNode(top(), jobj, _gvn.MakeConX(-JNIHandles::TypeTag::global))); + Node* jobj_untagged = _gvn.transform(AddPNode::make_off_heap(jobj, _gvn.MakeConX(-JNIHandles::TypeTag::global))); Node* event_writer = access_load(jobj_untagged, xtype, T_OBJECT, IN_NATIVE | C2_CONTROL_DEPENDENT_LOAD); // Load the current thread id from the event writer object. @@ -3636,9 +3581,9 @@ bool LibraryCallKit::inline_native_getEventWriter() { // Update control and phi nodes. event_writer_tid_compare_rgn->init_req(_true_path, tid_is_not_equal); event_writer_tid_compare_rgn->init_req(_false_path, tid_is_equal); - event_writer_tid_compare_mem->init_req(_true_path, _gvn.transform(reset_memory())); + event_writer_tid_compare_mem->init_req(_true_path, reset_memory()); event_writer_tid_compare_mem->init_req(_false_path, _gvn.transform(vthread_compare_mem)); - event_writer_tid_compare_io->init_req(_true_path, _gvn.transform(i_o())); + event_writer_tid_compare_io->init_req(_true_path, i_o()); event_writer_tid_compare_io->init_req(_false_path, _gvn.transform(vthread_compare_io)); // Result of top level CFG, Memory, IO and Value. @@ -3653,14 +3598,14 @@ bool LibraryCallKit::inline_native_getEventWriter() { // Result memory. result_mem->init_req(_true_path, _gvn.transform(event_writer_tid_compare_mem)); - result_mem->init_req(_false_path, _gvn.transform(input_memory_state)); + result_mem->init_req(_false_path, input_memory_state); // Result IO. result_io->init_req(_true_path, _gvn.transform(event_writer_tid_compare_io)); - result_io->init_req(_false_path, _gvn.transform(input_io_state)); + result_io->init_req(_false_path, input_io_state); // Result value. - result_value->init_req(_true_path, _gvn.transform(event_writer)); // return event writer oop + result_value->init_req(_true_path, event_writer); // return event writer oop result_value->init_req(_false_path, null()); // return null // Set output state. @@ -3707,7 +3652,7 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) { IfNode* iff_thread_not_equal_carrierThread = create_and_map_if(control(), test_thread_not_equal_carrierThread, PROB_FAIR, COUNT_UNKNOWN); - Node* vthread_offset = basic_plus_adr(top(), jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_OFFSET_JFR)); + Node* vthread_offset = off_heap_plus_addr(jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_OFFSET_JFR)); // False branch, is carrierThread. Node* thread_equal_carrierThread = _gvn.transform(new IfFalseNode(iff_thread_not_equal_carrierThread)); @@ -3726,17 +3671,17 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) { IN_HEAP | MO_UNORDERED | C2_MISMATCHED | C2_CONTROL_DEPENDENT_LOAD); // Mask off the excluded information from the epoch. - Node * const is_excluded = _gvn.transform(new AndINode(epoch_raw, _gvn.transform(excluded_mask))); + Node * const is_excluded = _gvn.transform(new AndINode(epoch_raw, excluded_mask)); // Load the tid field from the thread. Node* tid = load_field_from_object(thread, "tid", "J"); // Store the vthread tid to the jfr thread local. - Node* thread_id_offset = basic_plus_adr(top(), jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_ID_OFFSET_JFR)); + Node* thread_id_offset = off_heap_plus_addr(jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_ID_OFFSET_JFR)); Node* tid_memory = store_to_memory(control(), thread_id_offset, tid, T_LONG, MemNode::unordered, true); // Branch is_excluded to conditionalize updating the epoch . - Node* excluded_cmp = _gvn.transform(new CmpINode(is_excluded, _gvn.transform(excluded_mask))); + Node* excluded_cmp = _gvn.transform(new CmpINode(is_excluded, excluded_mask)); Node* test_excluded = _gvn.transform(new BoolNode(excluded_cmp, BoolTest::eq)); IfNode* iff_excluded = create_and_map_if(control(), test_excluded, PROB_MIN, COUNT_UNKNOWN); @@ -3751,10 +3696,10 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) { Node* vthread_is_included = _gvn.intcon(0); // Get epoch value. - Node* epoch = _gvn.transform(new AndINode(epoch_raw, _gvn.transform(epoch_mask))); + Node* epoch = _gvn.transform(new AndINode(epoch_raw, epoch_mask)); // Store the vthread epoch to the jfr thread local. - Node* vthread_epoch_offset = basic_plus_adr(top(), jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_EPOCH_OFFSET_JFR)); + Node* vthread_epoch_offset = off_heap_plus_addr(jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_EPOCH_OFFSET_JFR)); Node* included_memory = store_to_memory(control(), vthread_epoch_offset, epoch, T_CHAR, MemNode::unordered, true); RegionNode* excluded_rgn = new RegionNode(PATH_LIMIT); @@ -3769,15 +3714,15 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) { excluded_rgn->init_req(_false_path, included); excluded_mem->init_req(_true_path, tid_memory); excluded_mem->init_req(_false_path, included_memory); - exclusion->init_req(_true_path, _gvn.transform(vthread_is_excluded)); - exclusion->init_req(_false_path, _gvn.transform(vthread_is_included)); + exclusion->init_req(_true_path, vthread_is_excluded); + exclusion->init_req(_false_path, vthread_is_included); // Set intermediate state. set_control(_gvn.transform(excluded_rgn)); set_all_memory(excluded_mem); // Store the vthread exclusion state to the jfr thread local. - Node* thread_local_excluded_offset = basic_plus_adr(top(), jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_EXCLUDED_OFFSET_JFR)); + Node* thread_local_excluded_offset = off_heap_plus_addr(jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_EXCLUDED_OFFSET_JFR)); store_to_memory(control(), thread_local_excluded_offset, _gvn.transform(exclusion), T_BOOLEAN, MemNode::unordered, true); // Store release @@ -3823,16 +3768,15 @@ bool LibraryCallKit::inline_native_setCurrentThread() { "method changes current Thread but is not annotated ChangesCurrentThread"); Node* arr = argument(1); Node* thread = _gvn.transform(new ThreadLocalNode()); - Node* p = basic_plus_adr(top()/*!oop*/, thread, in_bytes(JavaThread::vthread_offset())); + Node* p = off_heap_plus_addr(thread, in_bytes(JavaThread::vthread_offset())); Node* thread_obj_handle = make_load(nullptr, p, p->bottom_type()->is_ptr(), T_OBJECT, MemNode::unordered); - thread_obj_handle = _gvn.transform(thread_obj_handle); const TypePtr *adr_type = _gvn.type(thread_obj_handle)->isa_ptr(); access_store_at(nullptr, thread_obj_handle, adr_type, arr, _gvn.type(arr), T_OBJECT, IN_NATIVE | MO_UNORDERED); // Change the _monitor_owner_id of the JavaThread Node* tid = load_field_from_object(arr, "tid", "J"); - Node* monitor_owner_id_offset = basic_plus_adr(top(), thread, in_bytes(JavaThread::monitor_owner_id_offset())); + Node* monitor_owner_id_offset = off_heap_plus_addr(thread, in_bytes(JavaThread::monitor_owner_id_offset())); store_to_memory(control(), monitor_owner_id_offset, tid, T_LONG, MemNode::unordered, true); JFR_ONLY(extend_setCurrentThread(thread, arr);) @@ -3853,7 +3797,7 @@ const Type* LibraryCallKit::scopedValueCache_type() { Node* LibraryCallKit::scopedValueCache_helper() { Node* thread = _gvn.transform(new ThreadLocalNode()); - Node* p = basic_plus_adr(top()/*!oop*/, thread, in_bytes(JavaThread::scopedValueCache_offset())); + Node* p = off_heap_plus_addr(thread, in_bytes(JavaThread::scopedValueCache_offset())); // We cannot use immutable_memory() because we might flip onto a // different carrier thread, at which point we'll need to use that // carrier thread's cache. @@ -3895,7 +3839,7 @@ bool LibraryCallKit::inline_native_Continuation_pinning(bool unpin) { // TLS Node* tls_ptr = _gvn.transform(new ThreadLocalNode()); - Node* last_continuation_offset = basic_plus_adr(top(), tls_ptr, in_bytes(JavaThread::cont_entry_offset())); + Node* last_continuation_offset = off_heap_plus_addr(tls_ptr, in_bytes(JavaThread::cont_entry_offset())); Node* last_continuation = make_load(control(), last_continuation_offset, last_continuation_offset->get_ptr_type(), T_ADDRESS, MemNode::unordered); // Null check the last continuation object. @@ -3912,7 +3856,7 @@ bool LibraryCallKit::inline_native_Continuation_pinning(bool unpin) { set_control(continuation_is_not_null); // Load the pin count from the last continuation. - Node* pin_count_offset = basic_plus_adr(top(), last_continuation, in_bytes(ContinuationEntry::pin_count_offset())); + Node* pin_count_offset = off_heap_plus_addr(last_continuation, in_bytes(ContinuationEntry::pin_count_offset())); Node* pin_count = make_load(control(), pin_count_offset, TypeInt::INT, T_INT, MemNode::unordered); // The loaded pin count is compared against a context specific rhs for over/underflow detection. @@ -3922,7 +3866,7 @@ bool LibraryCallKit::inline_native_Continuation_pinning(bool unpin) { } else { pin_count_rhs = _gvn.intcon(UINT32_MAX); } - Node* pin_count_cmp = _gvn.transform(new CmpUNode(_gvn.transform(pin_count), pin_count_rhs)); + Node* pin_count_cmp = _gvn.transform(new CmpUNode(pin_count, pin_count_rhs)); Node* test_pin_count_over_underflow = _gvn.transform(new BoolNode(pin_count_cmp, BoolTest::eq)); IfNode* iff_pin_count_over_underflow = create_and_map_if(control(), test_pin_count_over_underflow, PROB_MIN, COUNT_UNKNOWN); @@ -3959,10 +3903,10 @@ bool LibraryCallKit::inline_native_Continuation_pinning(bool unpin) { PhiNode* result_mem = new PhiNode(result_rgn, Type::MEMORY, TypePtr::BOTTOM); record_for_igvn(result_mem); - result_rgn->init_req(_true_path, _gvn.transform(valid_pin_count)); - result_rgn->init_req(_false_path, _gvn.transform(continuation_is_null)); - result_mem->init_req(_true_path, _gvn.transform(reset_memory())); - result_mem->init_req(_false_path, _gvn.transform(input_memory_state)); + result_rgn->init_req(_true_path, valid_pin_count); + result_rgn->init_req(_false_path, continuation_is_null); + result_mem->init_req(_true_path, reset_memory()); + result_mem->init_req(_false_path, input_memory_state); // Set output state. set_control(_gvn.transform(result_rgn)); @@ -3974,7 +3918,7 @@ bool LibraryCallKit::inline_native_Continuation_pinning(bool unpin) { //---------------------------load_mirror_from_klass---------------------------- // Given a klass oop, load its java mirror (a java.lang.Class oop). Node* LibraryCallKit::load_mirror_from_klass(Node* klass) { - Node* p = basic_plus_adr(top(), klass, in_bytes(Klass::java_mirror_offset())); + Node* p = off_heap_plus_addr(klass, in_bytes(Klass::java_mirror_offset())); Node* load = make_load(nullptr, p, TypeRawPtr::NOTNULL, T_ADDRESS, MemNode::unordered); // mirror = ((OopHandle)mirror)->resolve(); return access_load(load, TypeInstPtr::MIRROR, T_OBJECT, IN_NATIVE); @@ -4014,7 +3958,7 @@ Node* LibraryCallKit::generate_klass_flags_guard(Node* kls, int modifier_mask, i ByteSize offset, const Type* type, BasicType bt) { // Branch around if the given klass has the given modifier bit set. // Like generate_guard, adds a new path onto the region. - Node* modp = basic_plus_adr(top(), kls, in_bytes(offset)); + Node* modp = off_heap_plus_addr(kls, in_bytes(offset)); Node* mods = make_load(nullptr, modp, type, bt, MemNode::unordered); Node* mask = intcon(modifier_mask); Node* bits = intcon(modifier_bits); @@ -4148,7 +4092,7 @@ bool LibraryCallKit::inline_native_Class_query(vmIntrinsics::ID id) { phi->add_req(null()); } // If we fall through, it's a plain class. Get its _super. - p = basic_plus_adr(top(), kls, in_bytes(Klass::super_offset())); + p = off_heap_plus_addr(kls, in_bytes(Klass::super_offset())); kls = _gvn.transform(LoadKlassNode::make(_gvn, immutable_memory(), p, TypeRawPtr::BOTTOM, TypeInstKlassPtr::OBJECT_OR_NULL)); null_ctl = top(); kls = null_check_oop(kls, &null_ctl); @@ -4683,10 +4627,10 @@ Node* LibraryCallKit::generate_virtual_guard(Node* obj_klass, assert(vtable_index >= 0 || vtable_index == Method::nonvirtual_vtable_index, "bad index %d", vtable_index); // Get the Method* out of the appropriate vtable entry. - int entry_offset = in_bytes(Klass::vtable_start_offset()) + + int entry_offset = in_bytes(Klass::vtable_start_offset()) + vtable_index*vtableEntry::size_in_bytes() + in_bytes(vtableEntry::method_offset()); - Node* entry_addr = basic_plus_adr(top(), obj_klass, entry_offset); + Node* entry_addr = off_heap_plus_addr(obj_klass, entry_offset); Node* target_call = make_load(nullptr, entry_addr, TypePtr::NOTNULL, T_ADDRESS, MemNode::unordered); // Compare the target method with the expected method (e.g., Object.hashCode). @@ -5161,7 +5105,7 @@ bool LibraryCallKit::inline_unsafe_copyMemory() { Node* dst_addr = make_unsafe_address(dst_base, dst_off); Node* thread = _gvn.transform(new ThreadLocalNode()); - Node* doing_unsafe_access_addr = basic_plus_adr(top(), thread, in_bytes(JavaThread::doing_unsafe_access_offset())); + Node* doing_unsafe_access_addr = off_heap_plus_addr(thread, in_bytes(JavaThread::doing_unsafe_access_offset())); BasicType doing_unsafe_access_bt = T_BYTE; assert((sizeof(bool) * CHAR_BIT) == 8, "not implemented"); @@ -5216,7 +5160,7 @@ bool LibraryCallKit::inline_unsafe_setMemory() { Node* dst_addr = make_unsafe_address(dst_base, dst_off); Node* thread = _gvn.transform(new ThreadLocalNode()); - Node* doing_unsafe_access_addr = basic_plus_adr(top(), thread, in_bytes(JavaThread::doing_unsafe_access_offset())); + Node* doing_unsafe_access_addr = off_heap_plus_addr(thread, in_bytes(JavaThread::doing_unsafe_access_offset())); BasicType doing_unsafe_access_bt = T_BYTE; assert((sizeof(bool) * CHAR_BIT) == 8, "not implemented"); @@ -6225,9 +6169,10 @@ bool LibraryCallKit::inline_encodeISOArray(bool ascii) { } // Check source & target bounds - generate_string_range_check(src, src_offset, length, src_elem == T_BYTE, true); - generate_string_range_check(dst, dst_offset, length, false, true); - if (stopped()) { + RegionNode* bailout = create_bailout(); + generate_string_range_check(src, src_offset, length, src_elem == T_BYTE, bailout); + generate_string_range_check(dst, dst_offset, length, false, bailout); + if (check_bailout(bailout)) { return true; } @@ -6751,7 +6696,7 @@ bool LibraryCallKit::inline_updateCRC32() { Node* base = makecon(TypeRawPtr::make(StubRoutines::crc_table_addr())); Node* offset = _gvn.transform(new LShiftINode(result, intcon(0x2))); - Node* adr = basic_plus_adr(top(), base, ConvI2X(offset)); + Node* adr = off_heap_plus_addr(base, ConvI2X(offset)); result = make_load(control(), adr, TypeInt::INT, T_INT, MemNode::unordered); crc = _gvn.transform(new URShiftINode(crc, intcon(8))); @@ -6823,7 +6768,7 @@ bool LibraryCallKit::inline_updateByteBufferCRC32() { offset = ConvI2X(offset); // 'src_start' points to src array + scaled offset - Node* src_start = basic_plus_adr(top(), base, offset); + Node* src_start = off_heap_plus_addr(base, offset); // Call the stub. address stubAddr = StubRoutines::updateBytesCRC32(); @@ -6920,7 +6865,7 @@ bool LibraryCallKit::inline_updateDirectByteBufferCRC32C() { offset = ConvI2X(offset); // 'src_start' points to src array + scaled offset - Node* src_start = basic_plus_adr(top(), base, offset); + Node* src_start = off_heap_plus_addr(base, offset); // static final int[] byteTable in class CRC32C Node* table = get_table_from_crc32c_class(callee()->holder()); @@ -7004,7 +6949,7 @@ bool LibraryCallKit::inline_updateByteBufferAdler32() { offset = ConvI2X(offset); // 'src_start' points to src array + scaled offset - Node* src_start = basic_plus_adr(top(), base, offset); + Node* src_start = off_heap_plus_addr(base, offset); // Call the stub. address stubAddr = StubRoutines::updateBytesAdler32(); diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 56141be2362..9b87df645e1 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -129,30 +129,9 @@ class LibraryCallKit : public GraphKit { virtual int reexecute_sp() { return _reexecute_sp; } - /* When an intrinsic makes changes before bailing out, it's necessary to restore the graph - * as it was. See JDK-8359344 for what can happen wrong. It's also not always possible to - * bailout before making changes because the bailing out decision might depend on new nodes - * (their types, for instance). - * - * So, if an intrinsic might cause this situation, one must start by saving the state in a - * SavedState by constructing it, and the state will be restored on destruction. If the - * intrinsic is not bailing out, one need to call discard to prevent restoring the old state. - */ - class SavedState { - LibraryCallKit* _kit; - uint _sp; - JVMState* _jvms; - SafePointNode* _map; - Unique_Node_List _ctrl_succ; - bool _discarded; - - public: - SavedState(LibraryCallKit*); - ~SavedState(); - void discard(); - }; - // Helper functions to inline natives + RegionNode* create_bailout(); + bool check_bailout(RegionNode* bailout); Node* generate_guard(Node* test, RegionNode* region, float true_prob); Node* generate_slow_guard(Node* test, RegionNode* region); Node* generate_fair_guard(Node* test, RegionNode* region); @@ -166,7 +145,7 @@ class LibraryCallKit : public GraphKit { bool with_opaque = false); void generate_string_range_check(Node* array, Node* offset, Node* length, bool char_count, - bool halt_on_oob = false); + RegionNode* region); Node* current_thread_helper(Node* &tls_output, ByteSize handle_offset, bool is_immutable); Node* generate_current_thread(Node* &tls_output); diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 4e221a9a0ef..80b17efb998 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -524,6 +524,9 @@ bool IdealLoopTree::policy_peeling(PhaseIdealLoop *phase) { // return the estimated loop size if peeling is applicable, otherwise return // zero. No node budget is allocated. uint IdealLoopTree::estimate_peeling(PhaseIdealLoop *phase) { + if (LoopPeeling != 1) { + return 0; + } // If nodes are depleted, some transform has miscalculated its needs. assert(!phase->exceeding_node_budget(), "sanity"); @@ -775,6 +778,7 @@ void PhaseIdealLoop::peeled_dom_test_elim(IdealLoopTree* loop, Node_List& old_ne // exit // void PhaseIdealLoop::do_peeling(IdealLoopTree *loop, Node_List &old_new) { + assert(LoopPeeling != 0, "do_peeling called with loop peeling always disabled"); C->set_major_progress(); // Peeling a 'main' loop in a pre/main/post situation obfuscates the @@ -2201,6 +2205,15 @@ void PhaseIdealLoop::do_maximally_unroll(IdealLoopTree *loop, Node_List &old_new // If loop is tripping an odd number of times, peel odd iteration if ((cl->trip_count() & 1) == 1) { + if (LoopPeeling == 0) { +#ifndef PRODUCT + if (TraceLoopOpts) { + tty->print("MaxUnroll cancelled since LoopPeeling is always disabled"); + loop->dump_head(); + } +#endif + return; + } do_peeling(loop, old_new); } @@ -3243,6 +3256,15 @@ bool IdealLoopTree::do_remove_empty_loop(PhaseIdealLoop *phase) { #endif if (needs_guard) { + if (LoopPeeling == 0) { +#ifndef PRODUCT + if (TraceLoopOpts) { + tty->print("Empty loop not removed since LoopPeeling is always disabled"); + this->dump_head(); + } +#endif + return false; + } // Peel the loop to ensure there's a zero trip guard Node_List old_new; phase->do_peeling(this, old_new); @@ -3969,7 +3991,7 @@ bool PhaseIdealLoop::intrinsify_fill(IdealLoopTree* lpt) { index = new LShiftXNode(index, shift->in(2)); _igvn.register_new_node_with_optimizer(index); } - Node* from = new AddPNode(base, base, index); + Node* from = AddPNode::make_with_base(base, index); _igvn.register_new_node_with_optimizer(from); // For normal array fills, C2 uses two AddP nodes for array element // addressing. But for array fills with Unsafe call, there's only one @@ -3977,7 +3999,7 @@ bool PhaseIdealLoop::intrinsify_fill(IdealLoopTree* lpt) { assert(offset != nullptr || C->has_unsafe_access(), "Only array fills with unsafe have no extra offset"); if (offset != nullptr) { - from = new AddPNode(base, from, offset); + from = AddPNode::make_with_base(base, from, offset); _igvn.register_new_node_with_optimizer(from); } // Compute the number of elements to copy diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index d68505836d4..b2eb0c47458 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -375,17 +375,20 @@ IdealLoopTree* PhaseIdealLoop::create_outer_strip_mined_loop(Node* init_control, return outer_ilt; } -void PhaseIdealLoop::insert_loop_limit_check_predicate(ParsePredicateSuccessProj* loop_limit_check_parse_proj, - Node* cmp_limit, Node* bol) { + +void CountedLoopConverter::insert_loop_limit_check_predicate(const ParsePredicateSuccessProj* loop_limit_check_parse_proj, + Node* bol) const { assert(loop_limit_check_parse_proj->in(0)->is_ParsePredicate(), "must be parse predicate"); - Node* new_predicate_proj = create_new_if_for_predicate(loop_limit_check_parse_proj, nullptr, - Deoptimization::Reason_loop_limit_check, - Op_If); + Node* new_predicate_proj = _phase->create_new_if_for_predicate(loop_limit_check_parse_proj, nullptr, + Deoptimization::Reason_loop_limit_check, + Op_If); + + PhaseIterGVN& igvn = _phase->igvn(); Node* iff = new_predicate_proj->in(0); - cmp_limit = _igvn.register_new_node_with_optimizer(cmp_limit); - bol = _igvn.register_new_node_with_optimizer(bol); - set_subtree_ctrl(bol, false); - _igvn.replace_input_of(iff, 1, bol); + Node* cmp_limit = igvn.register_new_node_with_optimizer(bol->in(1)); + bol = igvn.register_new_node_with_optimizer(bol); + _phase->set_subtree_ctrl(bol, false); + igvn.replace_input_of(iff, 1, bol); #ifndef PRODUCT // report that the loop predication has been actually performed @@ -397,14 +400,38 @@ void PhaseIdealLoop::insert_loop_limit_check_predicate(ParsePredicateSuccessProj #endif } -Node* PhaseIdealLoop::loop_exit_control(Node* x, IdealLoopTree* loop) { +void CountedLoopConverter::insert_stride_overflow_limit_check() const { + const jlong stride_con = _structure.stride_con(); + + jlong adjusted_stride_con = (stride_con > 0 + ? max_signed_integer(_iv_bt) + : min_signed_integer(_iv_bt)) - _structure.final_limit_correction(); + Node* cmp_limit = CmpNode::make(_structure.limit(), + _phase->igvn().integercon(adjusted_stride_con, _iv_bt), _iv_bt); + Node* bol = new BoolNode(cmp_limit, stride_con > 0 ? BoolTest::le : BoolTest::ge); + + insert_loop_limit_check_predicate(_head->in(LoopNode::EntryControl)->as_IfTrue(), bol); +} + +void CountedLoopConverter::insert_init_trip_limit_check() const { + const jlong stride_con = _structure.stride_con(); + + Node* cmp_limit = CmpNode::make(_structure.phi()->in(LoopNode::EntryControl), _structure.limit(), _iv_bt); + Node* bol = new BoolNode(cmp_limit, stride_con > 0 ? BoolTest::lt : BoolTest::gt); + + insert_loop_limit_check_predicate(_head->in(LoopNode::EntryControl)->as_IfTrue(), bol); +} + +Node* PhaseIdealLoop::loop_exit_control(const IdealLoopTree* loop) const { + Node* head = loop->_head; + // Counted loop head must be a good RegionNode with only 3 not null // control input edges: Self, Entry, LoopBack. - if (x->in(LoopNode::Self) == nullptr || x->req() != 3 || loop->_irreducible) { + if (head->in(LoopNode::Self) == nullptr || head->req() != 3 || loop->_irreducible) { return nullptr; } - Node *init_control = x->in(LoopNode::EntryControl); - Node *back_control = x->in(LoopNode::LoopBackControl); + Node* init_control = head->in(LoopNode::EntryControl); + Node* back_control = head->in(LoopNode::LoopBackControl); if (init_control == nullptr || back_control == nullptr) { // Partially dead return nullptr; } @@ -437,77 +464,7 @@ Node* PhaseIdealLoop::loop_exit_control(Node* x, IdealLoopTree* loop) { return iftrue; } -Node* PhaseIdealLoop::loop_exit_test(Node* back_control, IdealLoopTree* loop, Node*& incr, Node*& limit, BoolTest::mask& bt, float& cl_prob) { - Node* iftrue = back_control; - uint iftrue_op = iftrue->Opcode(); - Node* iff = iftrue->in(0); - BoolNode* test = iff->in(1)->as_Bool(); - bt = test->_test._test; - cl_prob = iff->as_If()->_prob; - if (iftrue_op == Op_IfFalse) { - bt = BoolTest(bt).negate(); - cl_prob = 1.0 - cl_prob; - } - // Get backedge compare - Node* cmp = test->in(1); - if (!cmp->is_Cmp()) { - return nullptr; - } - - // Find the trip-counter increment & limit. Limit must be loop invariant. - incr = cmp->in(1); - limit = cmp->in(2); - - // --------- - // need 'loop()' test to tell if limit is loop invariant - // --------- - - if (!ctrl_is_member(loop, incr)) { // Swapped trip counter and limit? - Node* tmp = incr; // Then reverse order into the CmpI - incr = limit; - limit = tmp; - bt = BoolTest(bt).commute(); // And commute the exit test - } - if (ctrl_is_member(loop, limit)) { // Limit must be loop-invariant - return nullptr; - } - if (!ctrl_is_member(loop, incr)) { // Trip counter must be loop-variant - return nullptr; - } - return cmp; -} - -Node* PhaseIdealLoop::loop_iv_incr(Node* incr, Node* x, IdealLoopTree* loop, Node*& phi_incr) { - if (incr->is_Phi()) { - if (incr->as_Phi()->region() != x || incr->req() != 3) { - return nullptr; // Not simple trip counter expression - } - phi_incr = incr; - incr = phi_incr->in(LoopNode::LoopBackControl); // Assume incr is on backedge of Phi - if (!ctrl_is_member(loop, incr)) { // Trip counter must be loop-variant - return nullptr; - } - } - return incr; -} - -Node* PhaseIdealLoop::loop_iv_stride(Node* incr, Node*& xphi) { - assert(incr->Opcode() == Op_AddI || incr->Opcode() == Op_AddL, "caller resp."); - // Get merge point - xphi = incr->in(1); - Node *stride = incr->in(2); - if (!stride->is_Con()) { // Oops, swap these - if (!xphi->is_Con()) { // Is the other guy a constant? - return nullptr; // Nope, unknown stride, bail out - } - Node *tmp = xphi; // 'incr' is commutative, so ok to swap - xphi = stride; - stride = tmp; - } - return stride; -} - -PhiNode* PhaseIdealLoop::loop_iv_phi(Node* xphi, Node* phi_incr, Node* x) { +PhiNode* PhaseIdealLoop::loop_iv_phi(const Node* xphi, const Node* phi_incr, const Node* head) { if (!xphi->is_Phi()) { return nullptr; // Too much math on the trip counter } @@ -517,44 +474,31 @@ PhiNode* PhaseIdealLoop::loop_iv_phi(Node* xphi, Node* phi_incr, Node* x) { PhiNode *phi = xphi->as_Phi(); // Phi must be of loop header; backedge must wrap to increment - if (phi->region() != x) { + if (phi->region() != head) { return nullptr; } return phi; } -static int check_stride_overflow(jlong final_correction, const TypeInteger* limit_t, BasicType bt) { +CountedLoopConverter::StrideOverflowState CountedLoopConverter::check_stride_overflow(jlong final_correction, + const TypeInteger* limit_t, + BasicType bt) { if (final_correction > 0) { if (limit_t->lo_as_long() > (max_signed_integer(bt) - final_correction)) { - return -1; + return Overflow; } if (limit_t->hi_as_long() > (max_signed_integer(bt) - final_correction)) { - return 1; + return RequireLimitCheck; } } else { if (limit_t->hi_as_long() < (min_signed_integer(bt) - final_correction)) { - return -1; + return Overflow; } if (limit_t->lo_as_long() < (min_signed_integer(bt) - final_correction)) { - return 1; + return RequireLimitCheck; } } - return 0; -} - -static bool condition_stride_ok(BoolTest::mask bt, jlong stride_con) { - // If the condition is inverted and we will be rolling - // through MININT to MAXINT, then bail out. - if (bt == BoolTest::eq || // Bail out, but this loop trips at most twice! - // Odd stride - (bt == BoolTest::ne && stride_con != 1 && stride_con != -1) || - // Count down loop rolls through MAXINT - ((bt == BoolTest::le || bt == BoolTest::lt) && stride_con < 0) || - // Count up loop rolls through MININT - ((bt == BoolTest::ge || bt == BoolTest::gt) && stride_con > 0)) { - return false; // Bail out - } - return true; + return NoOverflow; } Node* PhaseIdealLoop::loop_nest_replace_iv(Node* iv_to_replace, Node* inner_iv, Node* outer_phi, Node* inner_head, @@ -647,10 +591,10 @@ void PhaseIdealLoop::add_parse_predicate(Deoptimization::DeoptReason reason, Nod // Find a safepoint node that dominates the back edge. We need a // SafePointNode so we can use its jvm state to create empty // predicates. -static bool no_side_effect_since_safepoint(Compile* C, Node* x, Node* mem, MergeMemNode* mm, PhaseIdealLoop* phase) { +static bool no_side_effect_since_safepoint(Compile* C, const Node* head, const Node* mem, MergeMemNode* mm, const PhaseIdealLoop* phase) { SafePointNode* safepoint = nullptr; - for (DUIterator_Fast imax, i = x->fast_outs(imax); i < imax; i++) { - Node* u = x->fast_out(i); + for (DUIterator_Fast imax, i = head->fast_outs(imax); i < imax; i++) { + Node* u = head->fast_out(i); if (u->is_memory_phi()) { Node* m = u->in(LoopNode::LoopBackControl); if (u->adr_type() == TypePtr::BOTTOM) { @@ -700,14 +644,14 @@ static bool no_side_effect_since_safepoint(Compile* C, Node* x, Node* mem, Merge return true; } -SafePointNode* PhaseIdealLoop::find_safepoint(Node* back_control, Node* x, IdealLoopTree* loop) { +SafePointNode* PhaseIdealLoop::find_safepoint(Node* back_control, const Node* head, const IdealLoopTree* loop) { IfNode* exit_test = back_control->in(0)->as_If(); SafePointNode* safepoint = nullptr; if (exit_test->in(0)->is_SafePoint() && exit_test->in(0)->outcnt() == 1) { safepoint = exit_test->in(0)->as_SafePoint(); } else { Node* c = back_control; - while (c != x && c->Opcode() != Op_SafePoint) { + while (c != head && c->Opcode() != Op_SafePoint) { c = idom(c); } @@ -746,7 +690,7 @@ SafePointNode* PhaseIdealLoop::find_safepoint(Node* back_control, Node* x, Ideal } } #endif - if (!no_side_effect_since_safepoint(C, x, mem, mm, this)) { + if (!no_side_effect_since_safepoint(C, head, mem, mm, this)) { safepoint = nullptr; } else { assert(mm == nullptr|| _igvn.transform(mm) == mem->as_MergeMem()->base_memory(), "all memory state should have been processed"); @@ -952,11 +896,11 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { // Loop is strip mined: use the safepoint of the outer strip mined loop OuterStripMinedLoopNode* outer_loop = head->as_CountedLoop()->outer_loop(); assert(outer_loop != nullptr, "no outer loop"); - safepoint = outer_loop->outer_safepoint(); + safepoint = LoopPeeling == 0 ? nullptr : outer_loop->outer_safepoint(); outer_loop->transform_to_counted_loop(&_igvn, this); exit_test = head->loopexit(); } else { - safepoint = find_safepoint(back_control, x, loop); + safepoint = LoopPeeling == 0 ? nullptr : find_safepoint(back_control, x, loop); } IfFalseNode* exit_branch = exit_test->false_proj(); @@ -1130,8 +1074,8 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { // Peel one iteration of the loop and use the safepoint at the end // of the peeled iteration to insert Parse Predicates. If no well // positioned safepoint peel to guarantee a safepoint in the outer - // loop. - if (safepoint != nullptr || !loop->_has_call) { + // loop. When loop peeling is disabled, skip the peeling step altogether. + if (LoopPeeling != 0 && (safepoint != nullptr || !loop->_has_call)) { old_new.clear(); do_peeling(loop, old_new); } else { @@ -1744,51 +1688,275 @@ LoopNode* PhaseIdealLoop::create_inner_head(IdealLoopTree* loop, BaseCountedLoop } #ifdef ASSERT -void PhaseIdealLoop::check_counted_loop_shape(IdealLoopTree* loop, Node* x, BasicType bt) { - Node* back_control = loop_exit_control(x, loop); +void PhaseIdealLoop::check_counted_loop_shape(IdealLoopTree* loop, Node* head, BasicType bt) { + Node* back_control = loop_exit_control(loop); assert(back_control != nullptr, "no back control"); - BoolTest::mask mask = BoolTest::illegal; - float cl_prob = 0; - Node* incr = nullptr; - Node* limit = nullptr; + LoopExitTest exit_test(back_control, loop, this); + exit_test.build(); + assert(exit_test.is_valid_with_bt(bt), "no exit test"); - Node* cmp = loop_exit_test(back_control, loop, incr, limit, mask, cl_prob); - assert(cmp != nullptr && cmp->Opcode() == Op_Cmp(bt), "no exit test"); + LoopIVIncr iv_incr(head, loop); + iv_incr.build(exit_test.incr()); + assert(iv_incr.is_valid_with_bt(bt), "no incr"); - Node* phi_incr = nullptr; - incr = loop_iv_incr(incr, x, loop, phi_incr); - assert(incr != nullptr && incr->Opcode() == Op_Add(bt), "no incr"); + LoopIVStride stride = LoopIVStride(bt); + stride.build(iv_incr.incr()); + assert(stride.is_valid(), "no stride"); - Node* xphi = nullptr; - Node* stride = loop_iv_stride(incr, xphi); + PhiNode* phi = loop_iv_phi(stride.xphi(), iv_incr.phi_incr(), head); + assert(phi != nullptr && phi->in(LoopNode::LoopBackControl) == iv_incr.incr(), "No phi"); - assert(stride != nullptr, "no stride"); + assert(stride.compute_non_zero_stride_con(exit_test.mask(), bt) != 0, "illegal condition"); - PhiNode* phi = loop_iv_phi(xphi, phi_incr, x); - - assert(phi != nullptr && phi->in(LoopNode::LoopBackControl) == incr, "No phi"); - - jlong stride_con = stride->get_integer_as_long(bt); - - assert(condition_stride_ok(mask, stride_con), "illegal condition"); - - assert(mask != BoolTest::ne, "unexpected condition"); - assert(phi_incr == nullptr, "bad loop shape"); - assert(cmp->in(1) == incr, "bad exit test shape"); + assert(exit_test.mask() != BoolTest::ne, "unexpected condition"); + assert(iv_incr.phi_incr() == nullptr, "bad loop shape"); + assert(exit_test.cmp()->in(1) == iv_incr.incr(), "bad exit test shape"); // Safepoint on backedge not supported - assert(x->in(LoopNode::LoopBackControl)->Opcode() != Op_SafePoint, "no safepoint on backedge"); + assert(head->in(LoopNode::LoopBackControl)->Opcode() != Op_SafePoint, "no safepoint on backedge"); } #endif +void PhaseIdealLoop::LoopExitTest::build() { + _is_valid = false; + + const Node* iftrue = _back_control; + uint iftrue_op = iftrue->Opcode(); + Node* iff = iftrue->in(0); + BoolNode* test = iff->in(1)->as_Bool(); + _mask = test->_test._test; + _cl_prob = iff->as_If()->_prob; + if (iftrue_op == Op_IfFalse) { + _mask = BoolTest(_mask).negate(); + _cl_prob = 1.0f - _cl_prob; + } + // Get backedge compare + _cmp = test->in(1); + if (!_cmp->is_Cmp()) { + return; + } + + // Find the trip-counter increment & limit. Limit must be loop invariant. + _incr = _cmp->in(1); + _limit = _cmp->in(2); + + // --------- + // need 'loop()' test to tell if limit is loop invariant + // --------- + + if (_loop->is_invariant(_incr)) { // Swapped trip counter and limit? + swap(_incr, _limit); // Then reverse order into the CmpI + _mask = BoolTest(_mask).commute(); // And commute the exit test + } + + if (!_loop->is_invariant(_limit)) { // Limit must be loop-invariant + return; + } + if (_loop->is_invariant(_incr)) { // Trip counter must be loop-variant + return; + } + + _is_valid = true; +} + +// Canonicalize the loop condition if it is 'ne'. +void PhaseIdealLoop::LoopExitTest::canonicalize_mask(jlong stride_con) { + if (_mask != BoolTest::ne) { + return; + } + + assert(stride_con == 1 || stride_con == -1, "simple increment only - checked in CountedLoopConverter"); + + if (stride_con == 1) { + // 'ne' can be replaced with 'lt' only when init < limit. + // This is ensured by the inserted predicate in CountedLoopConverter + _mask = BoolTest::lt; + } else { + // 'ne' can be replaced with 'gt' only when init > limit. + // This is ensured by the inserted predicate in CountedLoopConverter. + _mask = BoolTest::gt; + } +} + +void PhaseIdealLoop::LoopIVIncr::build(Node* old_incr) { + _is_valid = false; + + Node* incr = old_incr; + // Trip-counter increment must be commutative & associative. + if (incr->is_Phi()) { + if (incr->as_Phi()->region() != _head || incr->req() != 3) { + return; // Not simple trip counter expression + } + Node* phi_incr = incr; + Node* back_control = phi_incr->in(LoopNode::LoopBackControl); // Assume incr is on backedge of Phi + if (_loop->_phase->ctrl_is_member(_loop, back_control)) { // Trip counter must be loop-variant + _incr = back_control; + _phi_incr = phi_incr; + _is_valid = true; + return; + } + } + _incr = incr; + _phi_incr = nullptr; + + _is_valid = true; +} + +void PhaseIdealLoop::LoopIVStride::build(const Node* incr) { + _is_valid = false; + + assert(incr->Opcode() == Op_AddI || incr->Opcode() == Op_AddL, "caller resp."); + // Get merge point + _xphi = incr->in(1); + _stride_node = incr->in(2); + if (!_stride_node->is_Con()) { // Oops, swap these + if (!_xphi->is_Con()) { // Is the other guy a constant? + return; // Nope, unknown stride, bail out + } + swap(_xphi, _stride_node); // 'incr' is commutative, so ok to swap + } + + // Iteratively uncast the loop induction variable + // until no more CastII/CastLL nodes are found. + while (_xphi->Opcode() == Op_Cast(_iv_bt)) { + _xphi = _xphi->in(1); + } + + _is_valid = true; +} + +jlong PhaseIdealLoop::LoopIVStride::compute_non_zero_stride_con(const BoolTest::mask mask, const BasicType iv_bt) const { + jlong stride_con = stride_node()->get_integer_as_long(iv_bt); + assert(stride_con != 0, "missed some peephole opt"); // stride constant can never be 0! + + // If the condition is inverted and we will be rolling + // through MININT to MAXINT, then bail out. + if (mask == BoolTest::eq || // Bail out, but this loop trips at most twice! + // Odd stride + (mask == BoolTest::ne && stride_con != 1 && stride_con != -1) || + // Count down loop rolls through MAXINT + ((mask == BoolTest::le || mask == BoolTest::lt) && stride_con < 0) || + // Count up loop rolls through MININT + ((mask == BoolTest::ge || mask == BoolTest::gt) && stride_con > 0)) { + return 0; // Bail out with sentinel = 0 + } + + // Bail out if the stride is too big. + if (stride_con == min_signed_integer(iv_bt) || (ABS(stride_con) > max_signed_integer(iv_bt) / 2)) { + return 0; // Bail out with sentinel = 0 + } + + return stride_con; +} + +void CountedLoopConverter::LoopStructure::build() { + _is_valid = false; + + if (_back_control == nullptr) { + return; + } + + _exit_test.build(); + if (!_exit_test.is_valid_with_bt(_iv_bt)) { + return; // Avoid pointer & float & 64-bit compares + } + + Node* incr = _exit_test.incr(); + if (_exit_test.incr()->Opcode() == Op_Cast(_iv_bt)) { + incr = incr->in(1); + } + + _iv_incr.build(incr); + if (!_iv_incr.is_valid()) { + return; + } + + _truncated_increment.build(_iv_incr.incr()); + if (!_truncated_increment.is_valid()) { + return; // Funny increment opcode + } + assert(_truncated_increment.incr()->Opcode() == Op_Add(_iv_bt), "wrong increment code"); + + _stride.build(_truncated_increment.incr()); + if (!_stride.is_valid()) { + return; + } + + _phi = PhaseIdealLoop::loop_iv_phi(_stride.xphi(), _iv_incr.phi_incr(), _head); + if (_phi == nullptr || + (_truncated_increment.outer_trunc() == nullptr && _phi->in(LoopNode::LoopBackControl) != _truncated_increment.incr()) || + (_truncated_increment.outer_trunc() != nullptr && _phi->in(LoopNode::LoopBackControl) != _truncated_increment.outer_trunc())) { + return; + } + + Node* safepoint = _back_control->in(0)->in(0); + if (_loop->_child != nullptr) { + if (safepoint->Opcode() == Op_SafePoint) { + _safepoint = safepoint->as_SafePoint(); + } else { + _safepoint = nullptr; + } + } else { + _safepoint = _phase->find_safepoint(_back_control, _head, _loop); + } + + _is_valid = true; +} + +// We need to canonicalize the loop exit check by using different values for adjusted_limit: +// (LE1) iv_post_i < limit: Already canonicalized. We can directly use limit as adjusted_limit. +// -> adjusted_limit = limit. +// (LE2) iv_post_i <= limit: +// iv_post_i < limit + 1 +// -> adjusted limit = limit + 1 +// (LE3) iv_pre_i < limit: +// iv_pre_i + stride < limit + stride +// iv_post_i < limit + stride +// -> adjusted_limit = limit + stride +// (LE4) iv_pre_i <= limit: +// iv_pre_i < limit + 1 +// iv_pre_i + stride < limit + stride + 1 +// iv_post_i < limit + stride + 1 +// -> adjusted_limit = limit + stride + 1 +// +// Note that: +// (AL) limit <= adjusted_limit. +jlong CountedLoopConverter::LoopStructure::final_limit_correction() const { + const jlong stride_con = _stride.compute_non_zero_stride_con(_exit_test.mask(), _iv_bt); + + // Accounting for (LE3) and (LE4) where we use pre-incremented phis in the loop exit check. + const jlong limit_correction_for_pre_iv_exit_check = _iv_incr.phi_incr() != nullptr ? stride_con : 0; + + // Accounting for (LE2) and (LE4) where we use <= or >= in the loop exit check. + const jlong limit_correction_for_le_ge_exit_check = _exit_test.should_include_limit() + ? (stride_con > 0 ? 1 : -1) + : 0; + + const jlong limit_correction = limit_correction_for_pre_iv_exit_check + limit_correction_for_le_ge_exit_check; + const jlong canonicalized_correction = stride_con + (stride_con > 0 ? -1 : 1); + + return canonicalized_correction + limit_correction; // final_correction +} + #ifdef ASSERT -// convert an int counted loop to a long counted to stress handling of -// long counted loops -bool PhaseIdealLoop::convert_to_long_loop(Node* cmp, Node* phi, IdealLoopTree* loop) { +bool CountedLoopConverter::should_stress_long_counted_loop() { + assert(_checked_for_counted_loop, "must check for counted loop before stressing"); + + return StressLongCountedLoop > 0 && + _iv_bt == T_INT && + !_head->as_Loop()->is_loop_nest_inner_loop() && + _structure.truncated_increment().trunc_type() == TypeInt::INT; // Only stress an int loop (i.e., not char, byte or short) +} + +// Convert an int counted loop to a long counted to stress handling of long counted loops. Returns true upon success. +bool CountedLoopConverter::stress_long_counted_loop() { + assert(should_stress_long_counted_loop(), "stress condition not satisfied"); + + PhaseIterGVN* igvn = &_phase->igvn(); Unique_Node_List iv_nodes; Node_List old_new; - iv_nodes.push(cmp); + iv_nodes.push(_structure.exit_test().cmp()); bool failed = false; for (uint i = 0; i < iv_nodes.size() && !failed; i++) { @@ -1818,12 +1986,12 @@ bool PhaseIdealLoop::convert_to_long_loop(Node* cmp, Node* phi, IdealLoopTree* l fatal("unexpected"); } - for (uint i = 1; i < n->req(); i++) { - Node* in = n->in(i); + for (uint j = 1; j < n->req(); j++) { + Node* in = n->in(j); if (in == nullptr) { continue; } - if (ctrl_is_member(loop, in)) { + if (_loop->is_member(_phase->get_loop(_phase->get_ctrl(in)))) { iv_nodes.push(in); } } @@ -1834,243 +2002,142 @@ bool PhaseIdealLoop::convert_to_long_loop(Node* cmp, Node* phi, IdealLoopTree* l Node* n = iv_nodes.at(i); Node* clone = old_new[n->_idx]; if (clone != nullptr) { - _igvn.remove_dead_node(clone); + igvn->remove_dead_node(clone); } } return false; } + // Make sure we have loop limit checks in place to preserve overflows behaviour after casting to long. + if (_should_insert_stride_overflow_limit_check) { + insert_stride_overflow_limit_check(); + } + + if (_should_insert_init_trip_limit_check) { + insert_init_trip_limit_check(); + } + for (uint i = 0; i < iv_nodes.size(); i++) { Node* n = iv_nodes.at(i); Node* clone = old_new[n->_idx]; - for (uint i = 1; i < n->req(); i++) { - Node* in = n->in(i); + for (uint j = 1; j < n->req(); j++) { + Node* in = n->in(j); if (in == nullptr) { continue; } Node* in_clone = old_new[in->_idx]; if (in_clone == nullptr) { - assert(_igvn.type(in)->isa_int(), ""); + assert(igvn->type(in)->isa_int(), ""); in_clone = new ConvI2LNode(in); - _igvn.register_new_node_with_optimizer(in_clone); - set_subtree_ctrl(in_clone, false); + igvn->register_new_node_with_optimizer(in_clone); + _phase->set_subtree_ctrl(in_clone, false); } if (in_clone->in(0) == nullptr) { - in_clone->set_req(0, C->top()); - clone->set_req(i, in_clone); + in_clone->set_req(0, _phase->C->top()); + clone->set_req(j, in_clone); in_clone->set_req(0, nullptr); } else { - clone->set_req(i, in_clone); + clone->set_req(j, in_clone); } } - _igvn.register_new_node_with_optimizer(clone); + igvn->register_new_node_with_optimizer(clone); } - set_ctrl(old_new[phi->_idx], phi->in(0)); + _phase->set_ctrl(old_new[_structure.phi()->_idx], _structure.phi()->in(0)); for (uint i = 0; i < iv_nodes.size(); i++) { Node* n = iv_nodes.at(i); Node* clone = old_new[n->_idx]; - set_subtree_ctrl(clone, false); + _phase->set_subtree_ctrl(clone, false); Node* m = n->Opcode() == Op_CmpI ? clone : nullptr; - for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { - Node* u = n->fast_out(i); + for (DUIterator_Fast imax, j = n->fast_outs(imax); j < imax; j++) { + Node* u = n->fast_out(j); if (iv_nodes.member(u)) { continue; } if (m == nullptr) { m = new ConvL2INode(clone); - _igvn.register_new_node_with_optimizer(m); - set_subtree_ctrl(m, false); + igvn->register_new_node_with_optimizer(m); + _phase->set_subtree_ctrl(m, false); } - _igvn.rehash_node_delayed(u); - int nb = u->replace_edge(n, m, &_igvn); - --i, imax -= nb; + igvn->rehash_node_delayed(u); + int nb = u->replace_edge(n, m, igvn); + --j, imax -= nb; } } return true; } #endif -//------------------------------is_counted_loop-------------------------------- -bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv_bt) { - PhaseGVN *gvn = &_igvn; +bool PhaseIdealLoop::try_convert_to_counted_loop(Node* head, IdealLoopTree*& loop, const BasicType iv_bt) { + CountedLoopConverter converter(this, head, loop, iv_bt); + if (converter.is_counted_loop()) { +#ifdef ASSERT + // Stress by converting int counted loops to long counted loops + if (converter.should_stress_long_counted_loop() && converter.stress_long_counted_loop()) { + return false; + } +#endif - Node* back_control = loop_exit_control(x, loop); - if (back_control == nullptr) { + loop = converter.convert(); + return true; + } + + return false; +} + +bool CountedLoopConverter::is_counted_loop() { + PhaseIterGVN* igvn = &_phase->igvn(); + + _structure.build(); + if (!_structure.is_valid()) { return false; } - BoolTest::mask bt = BoolTest::illegal; - float cl_prob = 0; - Node* incr = nullptr; - Node* limit = nullptr; - Node* cmp = loop_exit_test(back_control, loop, incr, limit, bt, cl_prob); - if (cmp == nullptr || cmp->Opcode() != Op_Cmp(iv_bt)) { - return false; // Avoid pointer & float & 64-bit compares - } + // ================================================= + // ---- Is the loop trip counted? ---- - // Trip-counter increment must be commutative & associative. - if (incr->Opcode() == Op_Cast(iv_bt)) { - incr = incr->in(1); - } - - Node* phi_incr = nullptr; - incr = loop_iv_incr(incr, x, loop, phi_incr); - if (incr == nullptr) { + // Check trip counter will end up higher than the limit + if (_structure.is_infinite_loop()) { return false; } - Node* trunc1 = nullptr; - Node* trunc2 = nullptr; - const TypeInteger* iv_trunc_t = nullptr; - Node* orig_incr = incr; - if (!(incr = CountedLoopNode::match_incr_with_optional_truncation(incr, &trunc1, &trunc2, &iv_trunc_t, iv_bt))) { - return false; // Funny increment opcode - } - assert(incr->Opcode() == Op_Add(iv_bt), "wrong increment code"); - - Node* xphi = nullptr; - Node* stride = loop_iv_stride(incr, xphi); - - if (stride == nullptr) { - return false; - } - - // Iteratively uncast the loop induction variable - // until no more CastII/CastLL nodes are found. - while (xphi->Opcode() == Op_Cast(iv_bt)) { - xphi = xphi->in(1); - } - // Stride must be constant - jlong stride_con = stride->get_integer_as_long(iv_bt); - assert(stride_con != 0, "missed some peephole opt"); - - PhiNode* phi = loop_iv_phi(xphi, phi_incr, x); - - if (phi == nullptr || - (trunc1 == nullptr && phi->in(LoopNode::LoopBackControl) != incr) || - (trunc1 != nullptr && phi->in(LoopNode::LoopBackControl) != trunc1)) { + const jlong stride_con = _structure.stride_con(); + if (stride_con == 0) { return false; } - Node* iftrue = back_control; - uint iftrue_op = iftrue->Opcode(); - Node* iff = iftrue->in(0); - BoolNode* test = iff->in(1)->as_Bool(); - - const TypeInteger* limit_t = gvn->type(limit)->is_integer(iv_bt); - if (trunc1 != nullptr) { - // When there is a truncation, we must be sure that after the truncation - // the trip counter will end up higher than the limit, otherwise we are looking - // at an endless loop. Can happen with range checks. - - // Example: - // int i = 0; - // while (true) - // sum + = array[i]; - // i++; - // i = i && 0x7fff; - // } - // - // If the array is shorter than 0x8000 this exits through a AIOOB - // - Counted loop transformation is ok - // If the array is longer then this is an endless loop - // - No transformation can be done. - - const TypeInteger* incr_t = gvn->type(orig_incr)->is_integer(iv_bt); - if (limit_t->hi_as_long() > incr_t->hi_as_long()) { - // if the limit can have a higher value than the increment (before the phi) - return false; - } - } - - Node *init_trip = phi->in(LoopNode::EntryControl); - - // If iv trunc type is smaller than int, check for possible wrap. - if (!TypeInteger::bottom(iv_bt)->higher_equal(iv_trunc_t)) { - assert(trunc1 != nullptr, "must have found some truncation"); - - // Get a better type for the phi (filtered thru if's) - const TypeInteger* phi_ft = filtered_type(phi); - - // Can iv take on a value that will wrap? - // - // Ensure iv's limit is not within "stride" of the wrap value. - // - // Example for "short" type - // Truncation ensures value is in the range -32768..32767 (iv_trunc_t) - // If the stride is +10, then the last value of the induction - // variable before the increment (phi_ft->_hi) must be - // <= 32767 - 10 and (phi_ft->_lo) must be >= -32768 to - // ensure no truncation occurs after the increment. - - if (stride_con > 0) { - if (iv_trunc_t->hi_as_long() - phi_ft->hi_as_long() < stride_con || - iv_trunc_t->lo_as_long() > phi_ft->lo_as_long()) { - return false; // truncation may occur - } - } else if (stride_con < 0) { - if (iv_trunc_t->lo_as_long() - phi_ft->lo_as_long() > stride_con || - iv_trunc_t->hi_as_long() < phi_ft->hi_as_long()) { - return false; // truncation may occur - } - } - // No possibility of wrap so truncation can be discarded - // Promote iv type to Int - } else { - assert(trunc1 == nullptr && trunc2 == nullptr, "no truncation for int"); - } - - if (!condition_stride_ok(bt, stride_con)) { + // Check iv type can be promoted to int for short/char/byte loops + if (has_truncation_wrap(_structure.truncated_increment(), _structure.phi(), stride_con)) { return false; } - const TypeInteger* init_t = gvn->type(init_trip)->is_integer(iv_bt); - - if (stride_con > 0) { - if (init_t->lo_as_long() > max_signed_integer(iv_bt) - stride_con) { - return false; // cyclic loop - } - } else { - if (init_t->hi_as_long() < min_signed_integer(iv_bt) - stride_con) { - return false; // cyclic loop - } - } - - if (phi_incr != nullptr && bt != BoolTest::ne) { - // check if there is a possibility of IV overflowing after the first increment - if (stride_con > 0) { - if (init_t->hi_as_long() > max_signed_integer(iv_bt) - stride_con) { - return false; - } - } else { - if (init_t->lo_as_long() < min_signed_integer(iv_bt) - stride_con) { - return false; - } - } + // Check iv is not overflowing + Node* init_trip = _structure.phi()->in(LoopNode::EntryControl); + const TypeInteger* init_t = igvn->type(init_trip)->is_integer(_iv_bt); + if (is_iv_overflowing(init_t, stride_con, _structure.iv_incr().phi_incr(), _structure.exit_test().mask())) { + return false; } // ================================================= // ---- SUCCESS! Found A Trip-Counted Loop! ----- - // - if (x->Opcode() == Op_Region) { - // x has not yet been transformed to Loop or LongCountedLoop. + if (_head->Opcode() == Op_Region) { + // head has not yet been transformed to Loop or LongCountedLoop. // This should only happen if we are inside an infinite loop. // It happens like this: // build_loop_tree -> do not attach infinite loop and nested loops // beautify_loops -> does not transform the infinite and nested loops to LoopNode, because not attached yet // build_loop_tree -> find and attach infinite and nested loops // counted_loop -> nested Regions are not yet transformed to LoopNodes, we land here - assert(x->as_Region()->is_in_infinite_subgraph(), - "x can only be a Region and not Loop if inside infinite loop"); + assert(_head->as_Region()->is_in_infinite_subgraph(), + "head can only be a Region and not Loop if inside infinite loop"); // Come back later when Region is transformed to LoopNode return false; } - assert(x->Opcode() == Op_Loop || x->Opcode() == Op_LongCountedLoop, "regular loops only"); - C->print_method(PHASE_BEFORE_CLOOPS, 3, x); + assert(_head->Opcode() == Op_Loop || _head->Opcode() == Op_LongCountedLoop, "regular loops only"); + _phase->C->print_method(PHASE_BEFORE_CLOOPS, 3, _head); // =================================================== // We can only convert this loop to a counted loop if we can guarantee that the iv phi will never overflow at runtime. @@ -2123,23 +2190,10 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv // When converting a loop to a counted loop, we want to have a canonicalized loop exit check of the form: // iv_post_i < adjusted_limit // - // If that is not the case, we need to canonicalize the loop exit check by using different values for adjusted_limit: - // (LE1) iv_post_i < limit: Already canonicalized. We can directly use limit as adjusted_limit. - // -> adjusted_limit = limit. - // (LE2) iv_post_i <= limit: - // iv_post_i < limit + 1 - // -> adjusted limit = limit + 1 - // (LE3) iv_pre_i < limit: - // iv_pre_i + stride < limit + stride - // iv_post_i < limit + stride - // -> adjusted_limit = limit + stride - // (LE4) iv_pre_i <= limit: - // iv_pre_i < limit + 1 - // iv_pre_i + stride < limit + stride + 1 - // iv_post_i < limit + stride + 1 - // -> adjusted_limit = limit + stride + 1 + // If that is not the case, we need to canonicalize the loop exit check by using different values for adjusted_limit + // (see LoopStructure::final_limit_correction()). // - // Note that: + // Note that after canonicalization: // (AL) limit <= adjusted_limit. // // The following loop invariant has to hold for counted loops with n iterations (i.e. loop exit check true after n-th @@ -2259,78 +2313,63 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv // there is no overflow of the iv phi after the first iteration. In this case, we don't need to check (ii) // again and can skip the predicate. - // Check (vi) and bail out if the stride is too big. - if (stride_con == min_signed_integer(iv_bt) || (ABS(stride_con) > max_signed_integer(iv_bt) / 2)) { - return false; + const TypeInteger* limit_t = igvn->type(_structure.limit())->is_integer(_iv_bt); + StrideOverflowState stride_overflow_state = check_stride_overflow(_structure.final_limit_correction(), limit_t, _iv_bt); + + Node* init_control = _head->in(LoopNode::EntryControl); + const Predicates predicates(init_control); + const PredicateBlock* loop_limit_check_predicate_block = predicates.loop_limit_check_predicate_block(); + + if (stride_overflow_state == Overflow) { + return false; // Bailout: integer overflow is certain. } - // Accounting for (LE3) and (LE4) where we use pre-incremented phis in the loop exit check. - const jlong limit_correction_for_pre_iv_exit_check = (phi_incr != nullptr) ? stride_con : 0; - - // Accounting for (LE2) and (LE4) where we use <= or >= in the loop exit check. - const bool includes_limit = (bt == BoolTest::le || bt == BoolTest::ge); - const jlong limit_correction_for_le_ge_exit_check = (includes_limit ? (stride_con > 0 ? 1 : -1) : 0); - - const jlong limit_correction = limit_correction_for_pre_iv_exit_check + limit_correction_for_le_ge_exit_check; - const jlong canonicalized_correction = stride_con + (stride_con > 0 ? -1 : 1); - const jlong final_correction = canonicalized_correction + limit_correction; - - int sov = check_stride_overflow(final_correction, limit_t, iv_bt); - Node* init_control = x->in(LoopNode::EntryControl); - - // If sov==0, limit's type always satisfies the condition, for + // If stride_overflow_state == NO_OVERFLOW, limit's type always satisfies the condition, for // example, when it is an array length. - if (sov != 0) { - if (sov < 0) { - return false; // Bailout: integer overflow is certain. - } + + _should_insert_stride_overflow_limit_check = false; + if (stride_overflow_state == RequireLimitCheck) { // (1) Loop Limit Check Predicate is required because we could not statically prove that // limit + final_correction = adjusted_limit - 1 + stride <= max_int - assert(!x->as_Loop()->is_loop_nest_inner_loop(), "loop was transformed"); - const Predicates predicates(init_control); - const PredicateBlock* loop_limit_check_predicate_block = predicates.loop_limit_check_predicate_block(); + assert(!_head->as_Loop()->is_loop_nest_inner_loop(), "loop was transformed"); if (!loop_limit_check_predicate_block->has_parse_predicate()) { // The Loop Limit Check Parse Predicate is not generated if this method trapped here before. #ifdef ASSERT if (TraceLoopLimitCheck) { tty->print("Missing Loop Limit Check Parse Predicate:"); - loop->dump_head(); - x->dump(1); + _loop->dump_head(); + _head->dump(1); } #endif return false; } ParsePredicateNode* loop_limit_check_parse_predicate = loop_limit_check_predicate_block->parse_predicate(); - if (!is_dominator(get_ctrl(limit), loop_limit_check_parse_predicate->in(0))) { + if (!_phase->is_dominator(_phase->get_ctrl(_structure.limit()), loop_limit_check_parse_predicate->in(0))) { return false; } - Node* cmp_limit; - Node* bol; - - if (stride_con > 0) { - cmp_limit = CmpNode::make(limit, _igvn.integercon(max_signed_integer(iv_bt) - final_correction, iv_bt), iv_bt); - bol = new BoolNode(cmp_limit, BoolTest::le); - } else { - cmp_limit = CmpNode::make(limit, _igvn.integercon(min_signed_integer(iv_bt) - final_correction, iv_bt), iv_bt); - bol = new BoolNode(cmp_limit, BoolTest::ge); - } - - insert_loop_limit_check_predicate(init_control->as_IfTrue(), cmp_limit, bol); + _should_insert_stride_overflow_limit_check = true; } // (2.3) const bool init_plus_stride_could_overflow = - (stride_con > 0 && init_t->hi_as_long() > max_signed_integer(iv_bt) - stride_con) || - (stride_con < 0 && init_t->lo_as_long() < min_signed_integer(iv_bt) - stride_con); - // (2.1) - const bool init_gte_limit = (stride_con > 0 && init_t->hi_as_long() >= limit_t->lo_as_long()) || - (stride_con < 0 && init_t->lo_as_long() <= limit_t->hi_as_long()); + (stride_con > 0 && init_t->hi_as_long() > max_signed_integer(_iv_bt) - stride_con) || + (stride_con < 0 && init_t->lo_as_long() < min_signed_integer(_iv_bt) - stride_con); + // (2.1) + const bool init_gte_limit = + (stride_con > 0 && init_t->hi_as_long() >= limit_t->lo_as_long()) || + (stride_con < 0 && init_t->lo_as_long() <= limit_t->hi_as_long()); + + _should_insert_init_trip_limit_check = false; if (init_gte_limit && // (2.1) - ((bt == BoolTest::ne || init_plus_stride_could_overflow) && // (2.3) - !has_dominating_loop_limit_check(init_trip, limit, stride_con, iv_bt, init_control))) { // (2.2) + ((_structure.exit_test().mask() == BoolTest::ne || init_plus_stride_could_overflow) && // (2.3) + !has_dominating_loop_limit_check(init_trip, + _structure.limit(), + stride_con, + _iv_bt, + init_control))) { // (2.2) // (2) Iteration Loop Limit Check Predicate is required because neither (2.1), (2.2), nor (2.3) holds. // We use the following condition: // - stride > 0: init < limit @@ -2340,15 +2379,13 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv // a requirement). We transform the loop exit check by using a less-than-operator. By doing so, we must always // check that init < limit. Otherwise, we could have a different number of iterations at runtime. - const Predicates predicates(init_control); - const PredicateBlock* loop_limit_check_predicate_block = predicates.loop_limit_check_predicate_block(); if (!loop_limit_check_predicate_block->has_parse_predicate()) { // The Loop Limit Check Parse Predicate is not generated if this method trapped here before. #ifdef ASSERT if (TraceLoopLimitCheck) { tty->print("Missing Loop Limit Check Parse Predicate:"); - loop->dump_head(); - x->dump(1); + _loop->dump_head(); + _head->dump(1); } #endif return false; @@ -2356,81 +2393,188 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv ParsePredicateNode* loop_limit_check_parse_predicate = loop_limit_check_predicate_block->parse_predicate(); Node* parse_predicate_entry = loop_limit_check_parse_predicate->in(0); - if (!is_dominator(get_ctrl(limit), parse_predicate_entry) || - !is_dominator(get_ctrl(init_trip), parse_predicate_entry)) { + if (!_phase->is_dominator(_phase->get_ctrl(_structure.limit()), parse_predicate_entry) || + !_phase->is_dominator(_phase->get_ctrl(init_trip), parse_predicate_entry)) { return false; } - Node* cmp_limit; - Node* bol; - - if (stride_con > 0) { - cmp_limit = CmpNode::make(init_trip, limit, iv_bt); - bol = new BoolNode(cmp_limit, BoolTest::lt); - } else { - cmp_limit = CmpNode::make(init_trip, limit, iv_bt); - bol = new BoolNode(cmp_limit, BoolTest::gt); - } - - insert_loop_limit_check_predicate(init_control->as_IfTrue(), cmp_limit, bol); + _should_insert_init_trip_limit_check = true; } - if (bt == BoolTest::ne) { - // Now we need to canonicalize the loop condition if it is 'ne'. - assert(stride_con == 1 || stride_con == -1, "simple increment only - checked before"); - if (stride_con > 0) { - // 'ne' can be replaced with 'lt' only when init < limit. This is ensured by the inserted predicate above. - bt = BoolTest::lt; - } else { - assert(stride_con < 0, "must be"); - // 'ne' can be replaced with 'gt' only when init > limit. This is ensured by the inserted predicate above. - bt = BoolTest::gt; - } + _structure.exit_test().canonicalize_mask(stride_con); + + if (is_safepoint_invalid(_structure.sfpt())) { + return false; } - Node* sfpt = nullptr; - if (loop->_child == nullptr) { - sfpt = find_safepoint(back_control, x, loop); +#ifdef ASSERT + _checked_for_counted_loop = true; +#endif + + return true; +} + +bool CountedLoopConverter::is_iv_overflowing(const TypeInteger* init_t, jlong stride_con, Node* phi_increment, + BoolTest::mask mask) const { + if (stride_con > 0) { + if (init_t->lo_as_long() > max_signed_integer(_iv_bt) - stride_con) { + return true; // cyclic loop + } } else { - sfpt = iff->in(0); - if (sfpt->Opcode() != Op_SafePoint) { - sfpt = nullptr; + if (init_t->hi_as_long() < min_signed_integer(_iv_bt) - stride_con) { + return true; // cyclic loop } } - if (x->in(LoopNode::LoopBackControl)->Opcode() == Op_SafePoint) { - Node* backedge_sfpt = x->in(LoopNode::LoopBackControl); - if (((iv_bt == T_INT && LoopStripMiningIter != 0) || - iv_bt == T_LONG) && + if (phi_increment != nullptr && mask != BoolTest::ne) { + // check if there is a possibility of IV overflowing after the first increment + if (stride_con > 0) { + if (init_t->hi_as_long() > max_signed_integer(_iv_bt) - stride_con) { + return true; + } + } else { + if (init_t->lo_as_long() < min_signed_integer(_iv_bt) - stride_con) { + return true; + } + } + } + + return false; +} + +bool CountedLoopConverter::LoopStructure::is_infinite_loop() const { + PhaseIterGVN& igvn = _phase->igvn(); + const TypeInteger* limit_t = igvn.type(limit())->is_integer(_iv_bt); + + if (_truncated_increment.outer_trunc() != nullptr) { + // When there is a truncation, we must be sure that after the truncation + // the trip counter will end up higher than the limit, otherwise we are looking + // at an endless loop. Can happen with range checks. + + // Example: + // int i = 0; + // while (true) { + // sum + = array[i]; + // i++; + // i = i && 0x7fff; + // } + // + // If the array is shorter than 0x8000 this exits through an AIOOB + // - Counted loop transformation is ok + // If the array is longer then this is an endless loop + // - No transformation can be done. + + const TypeInteger* incr_t = igvn.type(_iv_incr.incr())->is_integer(_iv_bt); + if (limit_t->hi_as_long() > incr_t->hi_as_long()) { + // if the limit can have a higher value than the increment (before the phi) + return true; + } + } + + return false; +} + +bool CountedLoopConverter::has_truncation_wrap(const TruncatedIncrement& truncation, Node* phi, jlong stride_con) { + // If iv trunc type is smaller than int (i.e., short/char/byte), check for possible wrap. + if (!TypeInteger::bottom(_iv_bt)->higher_equal(truncation.trunc_type())) { + assert(truncation.outer_trunc() != nullptr, "must have found some truncation"); + + // Get a better type for the phi (filtered thru if's) + const TypeInteger* phi_ft = filtered_type(phi); + + // Can iv take on a value that will wrap? + // + // Ensure iv's limit is not within "stride" of the wrap value. + // + // Example for "short" type + // Truncation ensures value is in the range -32768..32767 (iv_trunc_t) + // If the stride is +10, then the last value of the induction + // variable before the increment (phi_ft->_hi) must be + // <= 32767 - 10 and (phi_ft->_lo) must be >= -32768 to + // ensure no truncation occurs after the increment. + + if (stride_con > 0) { + if (truncation.trunc_type()->hi_as_long() - phi_ft->hi_as_long() < stride_con || + truncation.trunc_type()->lo_as_long() > phi_ft->lo_as_long()) { + return true; // truncation may occur + } + } else if (stride_con < 0) { + if (truncation.trunc_type()->lo_as_long() - phi_ft->lo_as_long() > stride_con || + truncation.trunc_type()->hi_as_long() < phi_ft->hi_as_long()) { + return true; // truncation may occur + } + } + + // No possibility of wrap so truncation can be discarded + // Promote iv type to Int + } else { + assert(Type::equals(truncation.trunc_type(), TypeInt::INT) || Type::equals(truncation.trunc_type(), TypeLong::LONG), + "unexpected truncation type"); + assert(truncation.outer_trunc() == nullptr && truncation.inner_trunc() == nullptr, "no truncation for int"); + } + + return false; +} + +SafePointNode* CountedLoopConverter::find_safepoint(Node* iftrue) { + Node* iff = iftrue->in(0); + + if (_loop->_child == nullptr) { + return _phase->find_safepoint(iftrue, _head, _loop); + } + + Node* sfpt = iff->in(0); + if (sfpt->Opcode() == Op_SafePoint) { + return sfpt->as_SafePoint(); + } + return nullptr; +} + +bool CountedLoopConverter::is_safepoint_invalid(SafePointNode* sfpt) const { + if (_head->in(LoopNode::LoopBackControl)->Opcode() == Op_SafePoint) { + if (((_iv_bt == T_INT && LoopStripMiningIter != 0) || + _iv_bt == T_LONG) && sfpt == nullptr) { // Leaving the safepoint on the backedge and creating a // CountedLoop will confuse optimizations. We can't move the // safepoint around because its jvm state wouldn't match a new // location. Give up on that loop. - return false; - } - if (is_deleteable_safept(backedge_sfpt)) { - replace_node_and_forward_ctrl(backedge_sfpt, iftrue); - if (loop->_safepts != nullptr) { - loop->_safepts->yank(backedge_sfpt); - } - loop->_tail = iftrue; + return true; } } + return false; +} +IdealLoopTree* CountedLoopConverter::convert() { #ifdef ASSERT - if (iv_bt == T_INT && - !x->as_Loop()->is_loop_nest_inner_loop() && - StressLongCountedLoop > 0 && - trunc1 == nullptr && - convert_to_long_loop(cmp, phi, loop)) { - return false; - } + assert(_checked_for_counted_loop, "must check for counted loop before conversion"); #endif - Node* adjusted_limit = limit; - if (phi_incr != nullptr) { + PhaseIterGVN* igvn = &_phase->igvn(); + + if (_should_insert_stride_overflow_limit_check) { + insert_stride_overflow_limit_check(); + } + + if (_should_insert_init_trip_limit_check) { + insert_init_trip_limit_check(); + } + + Node* back_control = _phase->loop_exit_control(_loop); + if (_head->in(LoopNode::LoopBackControl)->Opcode() == Op_SafePoint) { + Node* backedge_sfpt = _head->in(LoopNode::LoopBackControl); + if (_phase->is_deleteable_safept(backedge_sfpt)) { + _phase->replace_node_and_forward_ctrl(backedge_sfpt, back_control); + if (_loop->_safepts != nullptr) { + _loop->_safepts->yank(backedge_sfpt); + } + _loop->_tail = back_control; + } + } + + Node* adjusted_limit = _structure.limit(); + if (_structure.iv_incr().phi_incr() != nullptr) { // If compare points directly to the phi we need to adjust // the compare so that it points to the incr. Limit have // to be adjusted to keep trip count the same and we @@ -2440,128 +2584,147 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv // is converted to // i = init; do {} while(++i < limit+1); // - adjusted_limit = gvn->transform(AddNode::make(limit, stride, iv_bt)); + adjusted_limit = igvn->transform(AddNode::make(_structure.limit(), _structure.stride().stride_node(), _iv_bt)); } - if (includes_limit) { + BoolTest::mask mask = _structure.exit_test().mask(); + if (_structure.exit_test().should_include_limit()) { // The limit check guaranties that 'limit <= (max_jint - stride)' so // we can convert 'i <= limit' to 'i < limit+1' since stride != 0. - // - Node* one = (stride_con > 0) ? gvn->integercon( 1, iv_bt) : gvn->integercon(-1, iv_bt); - adjusted_limit = gvn->transform(AddNode::make(adjusted_limit, one, iv_bt)); - if (bt == BoolTest::le) - bt = BoolTest::lt; - else if (bt == BoolTest::ge) - bt = BoolTest::gt; - else + Node* one = (_structure.stride_con() > 0) ? igvn->integercon(1, _iv_bt) : igvn->integercon(-1, _iv_bt); + adjusted_limit = igvn->transform(AddNode::make(adjusted_limit, one, _iv_bt)); + if (mask == BoolTest::le) { + mask = BoolTest::lt; + } else if (mask == BoolTest::ge) { + mask = BoolTest::gt; + } else { ShouldNotReachHere(); + } } - set_subtree_ctrl(adjusted_limit, false); + _phase->set_subtree_ctrl(adjusted_limit, false); // Build a canonical trip test. // Clone code, as old values may be in use. - incr = incr->clone(); - incr->set_req(1,phi); - incr->set_req(2,stride); - incr = _igvn.register_new_node_with_optimizer(incr); - set_early_ctrl(incr, false); - _igvn.rehash_node_delayed(phi); - phi->set_req_X( LoopNode::LoopBackControl, incr, &_igvn ); + Node* incr = _structure.truncated_increment().incr()->clone(); + incr->set_req(1, _structure.phi()); + incr->set_req(2, _structure.stride().stride_node()); + incr = igvn->register_new_node_with_optimizer(incr); + _phase->set_early_ctrl(incr, false); + igvn->rehash_node_delayed(_structure.phi()); + _structure.phi()->set_req_X(LoopNode::LoopBackControl, incr, igvn); // If phi type is more restrictive than Int, raise to // Int to prevent (almost) infinite recursion in igvn // which can only handle integer types for constants or minint..maxint. - if (!TypeInteger::bottom(iv_bt)->higher_equal(phi->bottom_type())) { - Node* nphi = PhiNode::make(phi->in(0), phi->in(LoopNode::EntryControl), TypeInteger::bottom(iv_bt)); + Node* phi = _structure.phi(); + if (!TypeInteger::bottom(_iv_bt)->higher_equal(phi->bottom_type())) { + Node* nphi = + PhiNode::make(phi->in(0), phi->in(LoopNode::EntryControl), TypeInteger::bottom(_iv_bt)); nphi->set_req(LoopNode::LoopBackControl, phi->in(LoopNode::LoopBackControl)); - nphi = _igvn.register_new_node_with_optimizer(nphi); - set_ctrl(nphi, get_ctrl(phi)); - _igvn.replace_node(phi, nphi); + nphi = igvn->register_new_node_with_optimizer(nphi); + _phase->set_ctrl(nphi, _phase->get_ctrl(phi)); + igvn->replace_node(phi, nphi); phi = nphi->as_Phi(); } - cmp = cmp->clone(); - cmp->set_req(1,incr); - cmp->set_req(2, adjusted_limit); - cmp = _igvn.register_new_node_with_optimizer(cmp); - set_ctrl(cmp, iff->in(0)); - test = test->clone()->as_Bool(); - (*(BoolTest*)&test->_test)._test = bt; - test->set_req(1,cmp); - _igvn.register_new_node_with_optimizer(test); - set_ctrl(test, iff->in(0)); + Node* iftrue = back_control; + const uint iftrue_op = iftrue->Opcode(); + Node* iff = iftrue->in(0); + + // Replace the old CmpNode with new adjusted_limit + Node* new_cmp = _structure.exit_test().cmp()->clone(); + new_cmp->set_req(1, incr); + new_cmp->set_req(2, adjusted_limit); + new_cmp = igvn->register_new_node_with_optimizer(new_cmp); + _phase->set_ctrl(new_cmp, iff->in(0)); + + // Replace the old BoolNode with new CmpNode + BoolNode* new_test = iff->in(1)->clone()->as_Bool(); + const_cast(&new_test->_test)->_test = mask; // Yes, it's a const, but it's a newly cloned node so we should be fine. + new_test->set_req(1, new_cmp); + igvn->register_new_node_with_optimizer(new_test); + _phase->set_ctrl(new_test, iff->in(0)); // Replace the old IfNode with a new LoopEndNode - Node *lex = _igvn.register_new_node_with_optimizer(BaseCountedLoopEndNode::make(iff->in(0), test, cl_prob, iff->as_If()->_fcnt, iv_bt)); - IfNode *le = lex->as_If(); - uint dd = dom_depth(iff); - set_idom(le, le->in(0), dd); // Update dominance for loop exit - set_loop(le, loop); + Node* loop_end = igvn->register_new_node_with_optimizer(BaseCountedLoopEndNode::make(iff->in(0), + new_test, + _structure.exit_test().cl_prob(), + iff->as_If()->_fcnt, + _iv_bt)); + IfNode* loop_end_exit = loop_end->as_If(); + const uint dd = _phase->dom_depth(iff); + _phase->set_idom(loop_end_exit, loop_end_exit->in(0), dd); // Update dominance for loop exit + _phase->set_loop(loop_end_exit, _loop); // Get the loop-exit control - Node *iffalse = iff->as_If()->proj_out(!(iftrue_op == Op_IfTrue)); + Node* iffalse = iff->as_If()->proj_out(!(iftrue_op == Op_IfTrue)); // Need to swap loop-exit and loop-back control? if (iftrue_op == Op_IfFalse) { - Node *ift2=_igvn.register_new_node_with_optimizer(new IfTrueNode (le)); - Node *iff2=_igvn.register_new_node_with_optimizer(new IfFalseNode(le)); + Node* ift2 = igvn->register_new_node_with_optimizer(new IfTrueNode(loop_end_exit)); + Node* iff2 = igvn->register_new_node_with_optimizer(new IfFalseNode(loop_end_exit)); - loop->_tail = back_control = ift2; - set_loop(ift2, loop); - set_loop(iff2, get_loop(iffalse)); + _loop->_tail = back_control = ift2; + _phase->set_loop(ift2, _loop); + _phase->set_loop(iff2, _phase->get_loop(iffalse)); // Lazy update of 'get_ctrl' mechanism. - replace_node_and_forward_ctrl(iffalse, iff2); - replace_node_and_forward_ctrl(iftrue, ift2); + _phase->replace_node_and_forward_ctrl(iffalse, iff2); + _phase->replace_node_and_forward_ctrl(iftrue, ift2); // Swap names iffalse = iff2; - iftrue = ift2; + iftrue = ift2; } else { - _igvn.rehash_node_delayed(iffalse); - _igvn.rehash_node_delayed(iftrue); - iffalse->set_req_X( 0, le, &_igvn ); - iftrue ->set_req_X( 0, le, &_igvn ); + igvn->rehash_node_delayed(iffalse); + igvn->rehash_node_delayed(iftrue); + iffalse->set_req_X(0, loop_end_exit, igvn); + iftrue->set_req_X(0, loop_end_exit, igvn); } - set_idom(iftrue, le, dd+1); - set_idom(iffalse, le, dd+1); + _phase->set_idom(iftrue, loop_end_exit, dd + 1); + _phase->set_idom(iffalse, loop_end_exit, dd + 1); assert(iff->outcnt() == 0, "should be dead now"); - replace_node_and_forward_ctrl(iff, le); // fix 'get_ctrl' + _phase->replace_node_and_forward_ctrl(iff, loop_end_exit); // fix 'get_ctrl' + Node* init_control = _head->in(LoopNode::EntryControl); Node* entry_control = init_control; - bool strip_mine_loop = iv_bt == T_INT && - loop->_child == nullptr && - sfpt != nullptr && - !loop->_has_call && - is_deleteable_safept(sfpt); + bool strip_mine_loop = _iv_bt == T_INT && + _loop->_child == nullptr && + _structure.sfpt() != nullptr && + !_loop->_has_call && + _phase->is_deleteable_safept(_structure.sfpt()); IdealLoopTree* outer_ilt = nullptr; if (strip_mine_loop) { - outer_ilt = create_outer_strip_mined_loop(init_control, loop, cl_prob, le->_fcnt, - entry_control, iffalse); + outer_ilt = _phase->create_outer_strip_mined_loop(init_control, + _loop, + _structure.exit_test().cl_prob(), + loop_end_exit->_fcnt, + entry_control, + iffalse); } // Now setup a new CountedLoopNode to replace the existing LoopNode - BaseCountedLoopNode *l = BaseCountedLoopNode::make(entry_control, back_control, iv_bt); - l->set_unswitch_count(x->as_Loop()->unswitch_count()); // Preserve + BaseCountedLoopNode* l = BaseCountedLoopNode::make(entry_control, back_control, _iv_bt); + l->set_unswitch_count(_head->as_Loop()->unswitch_count()); // Preserve // The following assert is approximately true, and defines the intention // of can_be_counted_loop. It fails, however, because phase->type // is not yet initialized for this loop and its parts. //assert(l->can_be_counted_loop(this), "sanity"); - _igvn.register_new_node_with_optimizer(l); - set_loop(l, loop); - loop->_head = l; + igvn->register_new_node_with_optimizer(l); + _phase->set_loop(l, _loop); + _loop->_head = l; // Fix all data nodes placed at the old loop head. // Uses the lazy-update mechanism of 'get_ctrl'. - replace_node_and_forward_ctrl(x, l); - set_idom(l, entry_control, dom_depth(entry_control) + 1); + _phase->replace_node_and_forward_ctrl(_head, l); + _phase->set_idom(l, entry_control, _phase->dom_depth(entry_control) + 1); - if (iv_bt == T_INT && (LoopStripMiningIter == 0 || strip_mine_loop)) { + if (_iv_bt == T_INT && (LoopStripMiningIter == 0 || strip_mine_loop)) { // Check for immediately preceding SafePoint and remove - if (sfpt != nullptr && (strip_mine_loop || is_deleteable_safept(sfpt))) { + if (_structure.sfpt() != nullptr && (strip_mine_loop || _phase->is_deleteable_safept(_structure.sfpt()))) { if (strip_mine_loop) { Node* outer_le = outer_ilt->_tail->in(0); - Node* sfpt_clone = sfpt->clone(); + Node* sfpt_clone = _structure.sfpt()->clone(); sfpt_clone->set_req(0, iffalse); outer_le->set_req(0, sfpt_clone); @@ -2570,40 +2733,42 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv // Polling load should be pinned outside inner loop. Node* new_polladdr = polladdr->clone(); new_polladdr->set_req(0, iffalse); - _igvn.register_new_node_with_optimizer(new_polladdr, polladdr); - set_ctrl(new_polladdr, iffalse); + igvn->register_new_node_with_optimizer(new_polladdr, polladdr); + _phase->set_ctrl(new_polladdr, iffalse); sfpt_clone->set_req(TypeFunc::Parms, new_polladdr); } // When this code runs, loop bodies have not yet been populated. const bool body_populated = false; - register_control(sfpt_clone, outer_ilt, iffalse, body_populated); - set_idom(outer_le, sfpt_clone, dom_depth(sfpt_clone)); + _phase->register_control(sfpt_clone, outer_ilt, iffalse, body_populated); + _phase->set_idom(outer_le, sfpt_clone, _phase->dom_depth(sfpt_clone)); } - replace_node_and_forward_ctrl(sfpt, sfpt->in(TypeFunc::Control)); - if (loop->_safepts != nullptr) { - loop->_safepts->yank(sfpt); + _phase->replace_node_and_forward_ctrl(_structure.sfpt(), _structure.sfpt()->in(TypeFunc::Control)); + if (_loop->_safepts != nullptr) { + _loop->_safepts->yank(_structure.sfpt()); } } } #ifdef ASSERT - assert(l->is_valid_counted_loop(iv_bt), "counted loop shape is messed up"); - assert(l == loop->_head && l->phi() == phi && l->loopexit_or_null() == lex, "" ); + assert(l->is_valid_counted_loop(_iv_bt), "counted loop shape is messed up"); + assert(l == _loop->_head && l->phi() == phi && l->loopexit_or_null() == loop_end, "" ); #endif + #ifndef PRODUCT if (TraceLoopOpts) { tty->print("Counted "); - loop->dump_head(); + _loop->dump_head(); } #endif - C->print_method(PHASE_AFTER_CLOOPS, 3, l); - // Capture bounds of the loop in the induction variable Phi before // subsequent transformation (iteration splitting) obscures the // bounds - l->phi()->as_Phi()->set_type(l->phi()->Value(&_igvn)); + l->phi()->as_Phi()->set_type(l->phi()->Value(igvn)); + _phase->C->print_method(PHASE_AFTER_CLOOPS, 3, l); + + IdealLoopTree* loop = _loop; if (strip_mine_loop) { l->mark_strip_mined(); l->verify_strip_mined(1); @@ -2612,21 +2777,24 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv } #ifndef PRODUCT - if (x->as_Loop()->is_loop_nest_inner_loop() && iv_bt == T_LONG) { + if (_head->as_Loop()->is_loop_nest_inner_loop() && _iv_bt == T_LONG) { AtomicAccess::inc(&_long_loop_counted_loops); } #endif - if (iv_bt == T_LONG && x->as_Loop()->is_loop_nest_outer_loop()) { + + if (_iv_bt == T_LONG && _head->as_Loop()->is_loop_nest_outer_loop()) { l->mark_loop_nest_outer_loop(); } - return true; + return loop; } // Check if there is a dominating loop limit check of the form 'init < limit' starting at the loop entry. // If there is one, then we do not need to create an additional Loop Limit Check Predicate. -bool PhaseIdealLoop::has_dominating_loop_limit_check(Node* init_trip, Node* limit, const jlong stride_con, - const BasicType iv_bt, Node* loop_entry) { +bool CountedLoopConverter::has_dominating_loop_limit_check(Node* init_trip, Node* limit, const jlong stride_con, + const BasicType iv_bt, Node* loop_entry) const { + PhaseIterGVN& _igvn = _phase->igvn(); + // Eagerly call transform() on the Cmp and Bool node to common them up if possible. This is required in order to // successfully find a dominated test with the If node below. Node* cmp_limit; @@ -2649,8 +2817,8 @@ bool PhaseIdealLoop::has_dominating_loop_limit_check(Node* init_trip, Node* limi const bool found_dominating_test = dominated_iff != nullptr && dominated_iff->is_ConI(); // Kill the If with its projections again in the next IGVN round by cutting it off from the graph. - _igvn.replace_input_of(iff, 0, C->top()); - _igvn.replace_input_of(iff, 1, C->top()); + _igvn.replace_input_of(iff, 0, _phase->C->top()); + _igvn.replace_input_of(iff, 1, _phase->C->top()); return found_dominating_test; } @@ -2953,24 +3121,23 @@ Node* LoopLimitNode::Identity(PhaseGVN* phase) { return this; } -//============================================================================= -//----------------------match_incr_with_optional_truncation-------------------- // Match increment with optional truncation: // CHAR: (i+1)&0x7fff, BYTE: ((i+1)<<8)>>8, or SHORT: ((i+1)<<16)>>16 -// Return null for failure. Success returns the increment node. -Node* CountedLoopNode::match_incr_with_optional_truncation(Node* expr, Node** trunc1, Node** trunc2, - const TypeInteger** trunc_type, - BasicType bt) { - // Quick cutouts: - if (expr == nullptr || expr->req() != 3) return nullptr; +void CountedLoopConverter::TruncatedIncrement::build(Node* expr) { + _is_valid = false; - Node *t1 = nullptr; - Node *t2 = nullptr; + // Quick cutouts: + if (expr == nullptr || expr->req() != 3) { + return; + } + + Node* t1 = nullptr; + Node* t2 = nullptr; Node* n1 = expr; int n1op = n1->Opcode(); - const TypeInteger* trunc_t = TypeInteger::bottom(bt); + const TypeInteger* trunc_t = TypeInteger::bottom(_bt); - if (bt == T_INT) { + if (_bt == T_INT) { // Try to strip (n1 & M) or (n1 << N >> N) from n1. if (n1op == Op_AndI && n1->in(2)->is_Con() && @@ -3002,15 +3169,14 @@ Node* CountedLoopNode::match_incr_with_optional_truncation(Node* expr, Node** tr } // If (maybe after stripping) it is an AddI, we won: - if (n1op == Op_Add(bt)) { - *trunc1 = t1; - *trunc2 = t2; - *trunc_type = trunc_t; - return n1; - } + if (n1op == Op_Add(_bt)) { + _incr = n1; + _outer_trunc = t1; + _inner_trunc = t2; + _trunc_type = trunc_t; - // failed - return nullptr; + _is_valid = true; + } } IfNode* CountedLoopNode::find_multiversion_if_from_multiversion_fast_main_loop() { @@ -3662,18 +3828,18 @@ Node *OuterStripMinedLoopEndNode::Ideal(PhaseGVN *phase, bool can_reshape) { // i = ? // } while ( i < 10) // -const TypeInt* PhaseIdealLoop::filtered_type( Node *n, Node* n_ctrl) { +const TypeInt* CountedLoopConverter::filtered_type(Node* n, Node* n_ctrl) { assert(n && n->bottom_type()->is_int(), "must be int"); const TypeInt* filtered_t = nullptr; if (!n->is_Phi()) { - assert(n_ctrl != nullptr || n_ctrl == C->top(), "valid control"); + assert(n_ctrl != nullptr || n_ctrl == _phase->C->top(), "valid control"); filtered_t = filtered_type_from_dominators(n, n_ctrl); } else { Node* phi = n->as_Phi(); Node* region = phi->in(0); assert(n_ctrl == nullptr || n_ctrl == region, "ctrl parameter must be region"); - if (region && region != C->top()) { + if (region && region != _phase->C->top()) { for (uint i = 1; i < phi->req(); i++) { Node* val = phi->in(i); Node* use_c = region->in(i); @@ -3688,7 +3854,7 @@ const TypeInt* PhaseIdealLoop::filtered_type( Node *n, Node* n_ctrl) { } } } - const TypeInt* n_t = _igvn.type(n)->is_int(); + const TypeInt* n_t = _phase->igvn().type(n)->is_int(); if (filtered_t != nullptr) { n_t = n_t->join(filtered_t)->is_int(); } @@ -3698,22 +3864,22 @@ const TypeInt* PhaseIdealLoop::filtered_type( Node *n, Node* n_ctrl) { //------------------------------filtered_type_from_dominators-------------------------------- // Return a possibly more restrictive type for val based on condition control flow of dominators -const TypeInt* PhaseIdealLoop::filtered_type_from_dominators( Node* val, Node *use_ctrl) { +const TypeInt* CountedLoopConverter::filtered_type_from_dominators(Node* val, Node* use_ctrl) { if (val->is_Con()) { return val->bottom_type()->is_int(); } uint if_limit = 10; // Max number of dominating if's visited const TypeInt* rtn_t = nullptr; - if (use_ctrl && use_ctrl != C->top()) { - Node* val_ctrl = get_ctrl(val); - uint val_dom_depth = dom_depth(val_ctrl); + if (use_ctrl && use_ctrl != _phase->C->top()) { + Node* val_ctrl = _phase->get_ctrl(val); + uint val_dom_depth = _phase->dom_depth(val_ctrl); Node* pred = use_ctrl; uint if_cnt = 0; while (if_cnt < if_limit) { if ((pred->Opcode() == Op_IfTrue || pred->Opcode() == Op_IfFalse)) { if_cnt++; - const TypeInt* if_t = IfNode::filtered_int_type(&_igvn, val, pred); + const TypeInt* if_t = IfNode::filtered_int_type(&_phase->igvn(), val, pred); if (if_t != nullptr) { if (rtn_t == nullptr) { rtn_t = if_t; @@ -3722,12 +3888,12 @@ const TypeInt* PhaseIdealLoop::filtered_type_from_dominators( Node* val, Node *u } } } - pred = idom(pred); - if (pred == nullptr || pred == C->top()) { + pred = _phase->idom(pred); + if (pred == nullptr || pred == _phase->C->top()) { break; } // Stop if going beyond definition block of val - if (dom_depth(pred) < val_dom_depth) { + if (_phase->dom_depth(pred) < val_dom_depth) { break; } } @@ -4335,7 +4501,7 @@ void IdealLoopTree::check_safepts(VectorSet &visited, Node_List &stack) { //---------------------------is_deleteable_safept---------------------------- // Is safept not required by an outer loop? -bool PhaseIdealLoop::is_deleteable_safept(Node* sfpt) { +bool PhaseIdealLoop::is_deleteable_safept(Node* sfpt) const { assert(sfpt->Opcode() == Op_SafePoint, ""); IdealLoopTree* lp = get_loop(sfpt)->_parent; while (lp != nullptr) { @@ -4553,9 +4719,7 @@ void IdealLoopTree::counted_loop( PhaseIdealLoop *phase ) { } IdealLoopTree* loop = this; - if (_head->is_CountedLoop() || - phase->is_counted_loop(_head, loop, T_INT)) { - + if (_head->is_CountedLoop() || phase->try_convert_to_counted_loop(_head, loop, T_INT)) { if (LoopStripMiningIter == 0 || _head->as_CountedLoop()->is_strip_mined()) { // Indicate we do not need a safepoint here _has_sfpt = 1; @@ -4567,8 +4731,7 @@ void IdealLoopTree::counted_loop( PhaseIdealLoop *phase ) { // Look for induction variables phase->replace_parallel_iv(this); - } else if (_head->is_LongCountedLoop() || - phase->is_counted_loop(_head, loop, T_LONG)) { + } else if (_head->is_LongCountedLoop() || phase->try_convert_to_counted_loop(_head, loop, T_LONG)) { remove_safepoints(phase, true); } else { assert(!_head->is_Loop() || !_head->as_Loop()->is_loop_nest_inner_loop(), "transformation to counted loop should not fail"); @@ -5373,9 +5536,15 @@ int PhaseIdealLoop::_loop_invokes=0;// Count of PhaseIdealLoop invokes int PhaseIdealLoop::_loop_work=0; // Sum of PhaseIdealLoop x unique volatile int PhaseIdealLoop::_long_loop_candidates=0; // Number of long loops seen volatile int PhaseIdealLoop::_long_loop_nests=0; // Number of long loops successfully transformed to a nest -volatile int PhaseIdealLoop::_long_loop_counted_loops=0; // Number of long loops successfully transformed to a counted loop +// Number of long loops successfully transformed to a counted loop +volatile int CountedLoopConverter::_long_loop_counted_loops = 0; void PhaseIdealLoop::print_statistics() { - tty->print_cr("PhaseIdealLoop=%d, sum _unique=%d, long loops=%d/%d/%d", _loop_invokes, _loop_work, _long_loop_counted_loops, _long_loop_nests, _long_loop_candidates); + tty->print_cr("PhaseIdealLoop=%d, sum _unique=%d, long loops=%d/%d/%d", + _loop_invokes, + _loop_work, + CountedLoopConverter::_long_loop_counted_loops, + _long_loop_nests, + _long_loop_candidates); } #endif diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 986cfdaa3f1..6667c71511c 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -273,11 +273,6 @@ public: CountedLoopEndNode* loopexit() const { return (CountedLoopEndNode*) BaseCountedLoopNode::loopexit(); } int stride_con() const; - // Match increment with optional truncation - static Node* - match_incr_with_optional_truncation(Node* expr, Node** trunc1, Node** trunc2, const TypeInteger** trunc_type, - BasicType bt); - // A 'main' loop has a pre-loop and a post-loop. The 'main' loop // can run short a few iterations and may start a few iterations in. // It will be RCE'd and unrolled and aligned. @@ -1029,8 +1024,6 @@ private: void rewire_old_target_loop_entry_dependency_to_new_entry(CountedLoopNode* target_loop_head, const Node* old_target_loop_entry, uint node_index_before_new_assertion_predicate_nodes); - void insert_loop_limit_check_predicate(ParsePredicateSuccessProj* loop_limit_check_parse_proj, Node* cmp_limit, - Node* bol); void log_loop_tree(); public: @@ -1294,7 +1287,7 @@ public: void recompute_dom_depth(); // Is safept not required by an outer loop? - bool is_deleteable_safept(Node* sfpt); + bool is_deleteable_safept(Node* sfpt) const; // Replace parallel induction variable (parallel to trip counter) void replace_parallel_iv(IdealLoopTree *loop); @@ -1345,21 +1338,109 @@ public: // Per-Node transform virtual Node* transform(Node* n) { return nullptr; } - Node* loop_exit_control(Node* x, IdealLoopTree* loop); - Node* loop_exit_test(Node* back_control, IdealLoopTree* loop, Node*& incr, Node*& limit, BoolTest::mask& bt, float& cl_prob); - Node* loop_iv_incr(Node* incr, Node* x, IdealLoopTree* loop, Node*& phi_incr); - Node* loop_iv_stride(Node* incr, Node*& xphi); - PhiNode* loop_iv_phi(Node* xphi, Node* phi_incr, Node* x); + Node* loop_exit_control(const IdealLoopTree* loop) const; - bool is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_bt); + class LoopExitTest { + bool _is_valid; + + const Node* _back_control; + const IdealLoopTree* _loop; + PhaseIdealLoop* _phase; + + Node* _cmp; + Node* _incr; + Node* _limit; + BoolTest::mask _mask; + float _cl_prob; + + public: + LoopExitTest(const Node* back_control, const IdealLoopTree* loop, PhaseIdealLoop* phase) : + _is_valid(false), + _back_control(back_control), + _loop(loop), + _phase(phase), + _cmp(nullptr), + _incr(nullptr), + _limit(nullptr), + _mask(BoolTest::illegal), + _cl_prob(0.0f) {} + + void build(); + void canonicalize_mask(jlong stride_con); + + bool is_valid_with_bt(BasicType bt) const { + return _is_valid && _cmp != nullptr && _cmp->Opcode() == Op_Cmp(bt); + } + + bool should_include_limit() const { return _mask == BoolTest::le || _mask == BoolTest::ge; } + + CmpNode* cmp() const { return _cmp->as_Cmp(); } + Node* incr() const { return _incr; } + Node* limit() const { return _limit; } + BoolTest::mask mask() const { return _mask; } + float cl_prob() const { return _cl_prob; } + }; + + class LoopIVIncr { + bool _is_valid; + + const Node* _head; + const IdealLoopTree* _loop; + + Node* _incr; + Node* _phi_incr; + + public: + LoopIVIncr(const Node* head, const IdealLoopTree* loop) : + _is_valid(false), + _head(head), + _loop(loop), + _incr(nullptr), + _phi_incr(nullptr) {} + + void build(Node* old_incr); + + bool is_valid() const { return _is_valid; } + bool is_valid_with_bt(const BasicType bt) const { + return _is_valid && _incr->Opcode() == Op_Add(bt); + } + + Node* incr() const { return _incr; } + Node* phi_incr() const { return _phi_incr; } + }; + + class LoopIVStride { + bool _is_valid; + + BasicType _iv_bt; + Node* _stride_node; + Node* _xphi; + + public: + LoopIVStride(BasicType iv_bt) : + _is_valid(false), + _iv_bt(iv_bt), + _stride_node(nullptr), + _xphi(nullptr) {} + + void build(const Node* incr); + + bool is_valid() const { return _is_valid && _stride_node != nullptr; } + Node* stride_node() const { return _stride_node; } + Node* xphi() const { return _xphi; } + + jlong compute_non_zero_stride_con(BoolTest::mask mask, BasicType iv_bt) const; + }; + + static PhiNode* loop_iv_phi(const Node* xphi, const Node* phi_incr, const Node* head); + + bool try_convert_to_counted_loop(Node* head, IdealLoopTree*& loop, BasicType iv_bt); Node* loop_nest_replace_iv(Node* iv_to_replace, Node* inner_iv, Node* outer_phi, Node* inner_head, BasicType bt); bool create_loop_nest(IdealLoopTree* loop, Node_List &old_new); -#ifdef ASSERT - bool convert_to_long_loop(Node* cmp, Node* phi, IdealLoopTree* loop); -#endif + void add_parse_predicate(Deoptimization::DeoptReason reason, Node* inner_head, IdealLoopTree* loop, SafePointNode* sfpt); - SafePointNode* find_safepoint(Node* back_control, Node* x, IdealLoopTree* loop); + SafePointNode* find_safepoint(Node* back_control, const Node* head, const IdealLoopTree* loop); void add_parse_predicates(IdealLoopTree* outer_ilt, LoopNode* inner_head, SafePointNode* cloned_sfpt); @@ -1496,8 +1577,6 @@ public: Node* clone_nodes_with_same_ctrl(Node* start_node, ProjNode* old_uncommon_proj, Node* new_uncommon_proj); void fix_cloned_data_node_controls(const ProjNode* orig, Node* new_uncommon_proj, const OrigToNewHashtable& orig_to_clone); - bool has_dominating_loop_limit_check(Node* init_trip, Node* limit, jlong stride_con, BasicType iv_bt, - Node* loop_entry); public: void register_control(Node* n, IdealLoopTree *loop, Node* pred, bool update_body = true); @@ -1755,12 +1834,6 @@ public: Node*& shift, Node*& offset); private: - // Return a type based on condition control flow - const TypeInt* filtered_type( Node *n, Node* n_ctrl); - const TypeInt* filtered_type( Node *n ) { return filtered_type(n, nullptr); } - // Helpers for filtered type - const TypeInt* filtered_type_from_dominators( Node* val, Node *val_ctrl); - // Helper functions Node *spinup( Node *iff, Node *new_false, Node *new_true, Node *region, Node *phi, small_cache *cache ); Node *find_use_block( Node *use, Node *def, Node *old_false, Node *new_false, Node *old_true, Node *new_true ); @@ -1889,7 +1962,6 @@ public: static int _loop_work; // Sum of PhaseIdealLoop x _unique static volatile int _long_loop_candidates; static volatile int _long_loop_nests; - static volatile int _long_loop_counted_loops; #endif #ifdef ASSERT @@ -1901,7 +1973,7 @@ public: void rpo(Node* start, Node_Stack &stk, VectorSet &visited, Node_List &rpo_list) const; - void check_counted_loop_shape(IdealLoopTree* loop, Node* x, BasicType bt) NOT_DEBUG_RETURN; + void check_counted_loop_shape(IdealLoopTree* loop, Node* head, BasicType bt) NOT_DEBUG_RETURN; LoopNode* create_inner_head(IdealLoopTree* loop, BaseCountedLoopNode* head, IfNode* exit_test); @@ -1979,6 +2051,146 @@ public: ConNode* zerocon(BasicType bt); }; +class CountedLoopConverter { + friend class PhaseIdealLoop; + + // Match increment with optional truncation + class TruncatedIncrement { + bool _is_valid; + + BasicType _bt; + + Node* _incr; + Node* _outer_trunc; + Node* _inner_trunc; + const TypeInteger* _trunc_type; + + public: + TruncatedIncrement(BasicType bt) : + _is_valid(false), + _bt(bt), + _incr(nullptr), + _outer_trunc(nullptr), + _inner_trunc(nullptr), + _trunc_type(nullptr) {} + + void build(Node* expr); + + bool is_valid() const { return _is_valid; } + Node* incr() const { return _incr; } + + // Optional truncation for: CHAR: (i+1)&0x7fff, BYTE: ((i+1)<<8)>>8, or SHORT: ((i+1)<<16)>>16 + Node* outer_trunc() const { return _outer_trunc; } // the outermost truncating node (either the & or the final >>) + Node* inner_trunc() const { return _inner_trunc; } // the inner truncating node, if applicable (the << in a <> pair) + const TypeInteger* trunc_type() const { return _trunc_type; } + }; + + class LoopStructure { + bool _is_valid; + + const Node* _head; + const IdealLoopTree* _loop; + PhaseIdealLoop* _phase; + BasicType _iv_bt; + + Node* _back_control; + PhaseIdealLoop::LoopExitTest _exit_test; + PhaseIdealLoop::LoopIVIncr _iv_incr; + TruncatedIncrement _truncated_increment; + PhaseIdealLoop::LoopIVStride _stride; + PhiNode* _phi; + SafePointNode* _safepoint; + + public: + LoopStructure(const Node* head, const IdealLoopTree* loop, PhaseIdealLoop* phase, const BasicType iv_bt) : + _is_valid(false), + _head(head), + _loop(loop), + _phase(phase), + _iv_bt(iv_bt), + _back_control(_phase->loop_exit_control(_loop)), + _exit_test(_back_control, _loop, _phase), + _iv_incr(_head, _loop), + _truncated_increment(_iv_bt), + _stride(PhaseIdealLoop::LoopIVStride(_iv_bt)), + _phi(nullptr), + _safepoint(nullptr) {} + + void build(); + + jlong final_limit_correction() const; // compute adjusted loop limit correction + bool is_infinite_loop() const; + + bool is_valid() const { return _is_valid; } + + Node* back_control() const { return _back_control; } + PhaseIdealLoop::LoopExitTest& exit_test() { return _exit_test; } + PhaseIdealLoop::LoopIVIncr& iv_incr() { return _iv_incr; } + TruncatedIncrement& truncated_increment() { return _truncated_increment; } + PhaseIdealLoop::LoopIVStride& stride() { return _stride; } + PhiNode* phi() const { return _phi; } + SafePointNode* sfpt() const { return _safepoint; } + jlong stride_con() const { return _stride.compute_non_zero_stride_con(_exit_test.mask(), _iv_bt); } + Node* limit() const { return _exit_test.limit(); } + }; + + PhaseIdealLoop* const _phase; + Node* const _head; + IdealLoopTree* const _loop; + const BasicType _iv_bt; + + LoopStructure _structure; + bool _should_insert_stride_overflow_limit_check = false; + bool _should_insert_init_trip_limit_check = false; + + DEBUG_ONLY(bool _checked_for_counted_loop = false;) + + // stats for PhaseIdealLoop::print_statistics() + static volatile int _long_loop_counted_loops; + + // Return a type based on condition control flow + const TypeInt* filtered_type(Node* n, Node* n_ctrl); + const TypeInt* filtered_type(Node* n) { return filtered_type(n, nullptr); } + // Helpers for filtered type + const TypeInt* filtered_type_from_dominators(Node* val, Node* val_ctrl); + + void insert_loop_limit_check_predicate(const ParsePredicateSuccessProj* loop_limit_check_parse_proj, Node* bol) const; + void insert_stride_overflow_limit_check() const; + void insert_init_trip_limit_check() const; + bool has_dominating_loop_limit_check(Node* init_trip, Node* limit, jlong stride_con, BasicType iv_bt, + Node* loop_entry) const; + + bool is_iv_overflowing(const TypeInteger* init_t, jlong stride_con, Node* phi_increment, BoolTest::mask mask) const; + bool has_truncation_wrap(const TruncatedIncrement& truncation, Node* phi, jlong stride_con); + SafePointNode* find_safepoint(Node* iftrue); + bool is_safepoint_invalid(SafePointNode* sfpt) const; + + public: + CountedLoopConverter(PhaseIdealLoop* phase, Node* head, IdealLoopTree* loop, const BasicType iv_bt) + : _phase(phase), + _head(head), + _loop(loop), + _iv_bt(iv_bt), + _structure(LoopStructure(_head, _loop, _phase, _iv_bt)) { + assert(phase != nullptr, "must be"); // Fail early if mandatory parameters are null. + assert(head != nullptr, "must be"); + assert(loop != nullptr, "must be"); + assert(iv_bt == T_INT || iv_bt == T_LONG, "either int or long loops"); + } + + bool is_counted_loop(); + IdealLoopTree* convert(); + + DEBUG_ONLY(bool should_stress_long_counted_loop();) + DEBUG_ONLY(bool stress_long_counted_loop();) + + enum StrideOverflowState { + Overflow = -1, + NoOverflow = 0, + RequireLimitCheck = 1 + }; + static StrideOverflowState check_stride_overflow(jlong final_correction, const TypeInteger* limit_t, BasicType bt); +}; class AutoNodeBudget : public StackObj { diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 862cb7067ec..4e447edee8d 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -620,10 +620,10 @@ Node* PhaseIdealLoop::remix_address_expressions(Node* n) { IdealLoopTree* n23_loop = get_loop(n23_ctrl); if (n22loop != n_loop && n22loop->is_member(n_loop) && n23_loop == n_loop) { - Node* add1 = new AddPNode(n->in(1), n->in(2)->in(2), n->in(3)); + Node* add1 = AddPNode::make_with_base(n->in(1), n->in(2)->in(2), n->in(3)); // Stuff new AddP in the loop preheader register_new_node(add1, n_loop->_head->as_Loop()->skip_strip_mined(1)->in(LoopNode::EntryControl)); - Node* add2 = new AddPNode(n->in(1), add1, n->in(2)->in(3)); + Node* add2 = AddPNode::make_with_base(n->in(1), add1, n->in(2)->in(3)); register_new_node(add2, n_ctrl); _igvn.replace_node(n, add2); return add2; @@ -641,10 +641,10 @@ Node* PhaseIdealLoop::remix_address_expressions(Node* n) { Node *tmp = V; V = I; I = tmp; } if (!ctrl_is_member(n_loop, I)) { - Node* add1 = new AddPNode(n->in(1), n->in(2), I); + Node* add1 = AddPNode::make_with_base(n->in(1), n->in(2), I); // Stuff new AddP in the loop preheader register_new_node(add1, n_loop->_head->as_Loop()->skip_strip_mined(1)->in(LoopNode::EntryControl)); - Node* add2 = new AddPNode(n->in(1), add1, V); + Node* add2 = AddPNode::make_with_base(n->in(1), add1, V); register_new_node(add2, n_ctrl); _igvn.replace_node(n, add2); return add2; @@ -1478,9 +1478,11 @@ void PhaseIdealLoop::split_if_with_blocks_post(Node *n) { // Now split the IF C->print_method(PHASE_BEFORE_SPLIT_IF, 4, iff); - if (TraceLoopOpts) { - tty->print_cr("Split-If"); +#ifndef PRODUCT + if (TraceLoopOpts || TraceSplitIf) { + tty->print_cr("Split-If: %d %s", iff->_idx, iff->Name()); } +#endif do_split_if(iff); C->print_method(PHASE_AFTER_SPLIT_IF, 4, iff); return; @@ -1590,6 +1592,11 @@ bool PhaseIdealLoop::try_merge_identical_ifs(Node* n) { // Now split the IF RegionNode* new_false_region; RegionNode* new_true_region; +#ifndef PRODUCT + if (TraceLoopOpts || TraceSplitIf) { + tty->print_cr("Split-If Merging Identical Ifs: Dom-If: %d %s, If: %d %s", dom_if->_idx, dom_if->Name(), n->_idx, n->Name()); + } +#endif do_split_if(n, &new_false_region, &new_true_region); assert(new_false_region->req() == new_true_region->req(), ""); #ifdef ASSERT @@ -2829,8 +2836,6 @@ void PhaseIdealLoop::clone_loop_body(const Node_List& body, Node_List &old_new, // with an optional truncation (left-shift followed by a right-shift) // of the add. Returns zero if not an iv. int PhaseIdealLoop::stride_of_possible_iv(Node* iff) { - Node* trunc1 = nullptr; - Node* trunc2 = nullptr; const TypeInteger* ttype = nullptr; if (!iff->is_If() || iff->in(1) == nullptr || !iff->in(1)->is_Bool()) { return 0; @@ -2851,23 +2856,23 @@ int PhaseIdealLoop::stride_of_possible_iv(Node* iff) { Node* phi = cmp1; for (uint i = 1; i < phi->req(); i++) { Node* in = phi->in(i); - Node* add = CountedLoopNode::match_incr_with_optional_truncation(in, - &trunc1, &trunc2, &ttype, T_INT); - if (add && add->in(1) == phi) { - add2 = add->in(2); + CountedLoopConverter::TruncatedIncrement add(T_INT); + add.build(in); + if (add.is_valid() && add.incr()->in(1) == phi) { + add2 = add.incr()->in(2); break; } } } else { // (If (Bool (CmpX addtrunc:(Optional-trunc((AddI (Phi ...addtrunc...) add2)) ))) Node* addtrunc = cmp1; - Node* add = CountedLoopNode::match_incr_with_optional_truncation(addtrunc, - &trunc1, &trunc2, &ttype, T_INT); - if (add && add->in(1)->is_Phi()) { - Node* phi = add->in(1); + CountedLoopConverter::TruncatedIncrement add(T_INT); + add.build(addtrunc); + if (add.is_valid() && add.incr()->in(1)->is_Phi()) { + Node* phi = add.incr()->in(1); for (uint i = 1; i < phi->req(); i++) { if (phi->in(i) == addtrunc) { - add2 = add->in(2); + add2 = add.incr()->in(2); break; } } @@ -4294,54 +4299,50 @@ bool PhaseIdealLoop::duplicate_loop_backedge(IdealLoopTree *loop, Node_List &old #endif //ASSERT { // Is the shape of the loop that of a counted loop... - Node* back_control = loop_exit_control(head, loop); + Node* back_control = loop_exit_control(loop); if (back_control == nullptr) { return false; } - BoolTest::mask bt = BoolTest::illegal; - float cl_prob = 0; - Node* incr = nullptr; - Node* limit = nullptr; - Node* cmp = loop_exit_test(back_control, loop, incr, limit, bt, cl_prob); - if (cmp == nullptr || cmp->Opcode() != Op_CmpI) { + LoopExitTest loop_exit(back_control, loop, this); + loop_exit.build(); + if (!loop_exit.is_valid_with_bt(T_INT)) { return false; } + const Node* loop_incr = loop_exit.incr(); + // With an extra phi for the candidate iv? // Or the region node is the loop head - if (!incr->is_Phi() || incr->in(0) == head) { + if (!loop_incr->is_Phi() || loop_incr->in(0) == head) { return false; } PathFrequency pf(head, this); - region = incr->in(0); + region = loop_incr->in(0); // Go over all paths for the extra phi's region and see if that // path is frequent enough and would match the expected iv shape // if the extra phi is removed inner = 0; - for (uint i = 1; i < incr->req(); ++i) { - Node* in = incr->in(i); - Node* trunc1 = nullptr; - Node* trunc2 = nullptr; - const TypeInteger* iv_trunc_t = nullptr; - Node* orig_in = in; - if (!(in = CountedLoopNode::match_incr_with_optional_truncation(in, &trunc1, &trunc2, &iv_trunc_t, T_INT))) { + for (uint i = 1; i < loop_incr->req(); ++i) { + CountedLoopConverter::TruncatedIncrement increment(T_INT); + increment.build(loop_incr->in(i)); + if (!increment.is_valid()) { continue; } - assert(in->Opcode() == Op_AddI, "wrong increment code"); - Node* xphi = nullptr; - Node* stride = loop_iv_stride(in, xphi); + assert(increment.incr()->Opcode() == Op_AddI, "wrong increment code"); - if (stride == nullptr) { + LoopIVStride stride = LoopIVStride(T_INT); + stride.build(increment.incr()); + if (!stride.is_valid()) { continue; } - PhiNode* phi = loop_iv_phi(xphi, nullptr, head); + PhiNode* phi = loop_iv_phi(stride.xphi(), nullptr, head); if (phi == nullptr || - (trunc1 == nullptr && phi->in(LoopNode::LoopBackControl) != incr) || - (trunc1 != nullptr && phi->in(LoopNode::LoopBackControl) != trunc1)) { + (increment.outer_trunc() == nullptr && phi->in(LoopNode::LoopBackControl) != loop_exit.incr()) || + (increment.outer_trunc() != nullptr && phi->in(LoopNode::LoopBackControl) != increment.outer_trunc())) { return false; } diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index ef38a511a88..48277eb46d2 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -274,7 +274,7 @@ Node* PhaseMacroExpand::make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, if (ac->is_clonebasic()) { assert(ac->in(ArrayCopyNode::Src) != ac->in(ArrayCopyNode::Dest), "clone source equals destination"); Node* base = ac->in(ArrayCopyNode::Src); - Node* adr = _igvn.transform(new AddPNode(base, base, _igvn.MakeConX(offset))); + Node* adr = _igvn.transform(AddPNode::make_with_base(base, _igvn.MakeConX(offset))); const TypePtr* adr_type = _igvn.type(base)->is_ptr()->add_offset(offset); MergeMemNode* mergemen = _igvn.transform(MergeMemNode::make(mem))->as_MergeMem(); BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); @@ -293,7 +293,7 @@ Node* PhaseMacroExpand::make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, if (src_pos_t->is_con() && dest_pos_t->is_con()) { intptr_t off = ((src_pos_t->get_con() - dest_pos_t->get_con()) << shift) + offset; Node* base = ac->in(ArrayCopyNode::Src); - adr = _igvn.transform(new AddPNode(base, base, _igvn.MakeConX(off))); + adr = _igvn.transform(AddPNode::make_with_base(base, _igvn.MakeConX(off))); adr_type = _igvn.type(base)->is_ptr()->add_offset(off); if (ac->in(ArrayCopyNode::Src) == ac->in(ArrayCopyNode::Dest)) { // Don't emit a new load from src if src == dst but try to get the value from memory instead @@ -308,7 +308,7 @@ Node* PhaseMacroExpand::make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, Node* off = _igvn.transform(new AddXNode(_igvn.MakeConX(offset), diff)); Node* base = ac->in(ArrayCopyNode::Src); - adr = _igvn.transform(new AddPNode(base, base, off)); + adr = _igvn.transform(AddPNode::make_with_base(base, off)); adr_type = _igvn.type(base)->is_ptr()->add_offset(Type::OffsetBot); if (ac->in(ArrayCopyNode::Src) == ac->in(ArrayCopyNode::Dest)) { // Non constant offset in the array: we can't statically @@ -1199,7 +1199,7 @@ bool PhaseMacroExpand::eliminate_boxing_node(CallStaticJavaNode *boxing) { Node* PhaseMacroExpand::make_load_raw(Node* ctl, Node* mem, Node* base, int offset, const Type* value_type, BasicType bt) { - Node* adr = basic_plus_adr(top(), base, offset); + Node* adr = off_heap_plus_addr(base, offset); const TypePtr* adr_type = adr->bottom_type()->is_ptr(); Node* value = LoadNode::make(_igvn, ctl, mem, adr, adr_type, value_type, bt, MemNode::unordered); transform_later(value); @@ -1208,7 +1208,7 @@ Node* PhaseMacroExpand::make_load_raw(Node* ctl, Node* mem, Node* base, int offs Node* PhaseMacroExpand::make_store_raw(Node* ctl, Node* mem, Node* base, int offset, Node* value, BasicType bt) { - Node* adr = basic_plus_adr(top(), base, offset); + Node* adr = off_heap_plus_addr(base, offset); mem = StoreNode::make(_igvn, ctl, mem, adr, nullptr, value, bt, MemNode::unordered); transform_later(mem); return mem; @@ -1313,7 +1313,9 @@ void PhaseMacroExpand::expand_allocate_common( initial_slow_test = nullptr; } - bool allocation_has_use = (alloc->result_cast() != nullptr); + // ArrayCopyNode right after an allocation operates on the raw result projection for the Allocate node so it's not + // safe to remove such an allocation even if it has no result cast. + bool allocation_has_use = (alloc->result_cast() != nullptr) || (alloc->initialization() != nullptr && alloc->initialization()->is_complete_with_arraycopy()); if (!allocation_has_use) { InitializeNode* init = alloc->initialization(); if (init != nullptr) { @@ -1818,81 +1820,81 @@ Node* PhaseMacroExpand::prefetch_allocation(Node* i_o, Node*& needgc_false, Node* old_eden_top, Node* new_eden_top, intx lines) { enum { fall_in_path = 1, pf_path = 2 }; - if( UseTLAB && AllocatePrefetchStyle == 2 ) { + if (UseTLAB && AllocatePrefetchStyle == 2) { // Generate prefetch allocation with watermark check. // As an allocation hits the watermark, we will prefetch starting // at a "distance" away from watermark. - Node *pf_region = new RegionNode(3); - Node *pf_phi_rawmem = new PhiNode( pf_region, Type::MEMORY, - TypeRawPtr::BOTTOM ); + Node* pf_region = new RegionNode(3); + Node* pf_phi_rawmem = new PhiNode(pf_region, Type::MEMORY, + TypeRawPtr::BOTTOM); // I/O is used for Prefetch - Node *pf_phi_abio = new PhiNode( pf_region, Type::ABIO ); + Node* pf_phi_abio = new PhiNode(pf_region, Type::ABIO); - Node *thread = new ThreadLocalNode(); + Node* thread = new ThreadLocalNode(); transform_later(thread); - Node *eden_pf_adr = new AddPNode( top()/*not oop*/, thread, - _igvn.MakeConX(in_bytes(JavaThread::tlab_pf_top_offset())) ); + Node* eden_pf_adr = AddPNode::make_off_heap(thread, + _igvn.MakeConX(in_bytes(JavaThread::tlab_pf_top_offset()))); transform_later(eden_pf_adr); - Node *old_pf_wm = new LoadPNode(needgc_false, + Node* old_pf_wm = new LoadPNode(needgc_false, contended_phi_rawmem, eden_pf_adr, TypeRawPtr::BOTTOM, TypeRawPtr::BOTTOM, MemNode::unordered); transform_later(old_pf_wm); // check against new_eden_top - Node *need_pf_cmp = new CmpPNode( new_eden_top, old_pf_wm ); + Node* need_pf_cmp = new CmpPNode(new_eden_top, old_pf_wm); transform_later(need_pf_cmp); - Node *need_pf_bol = new BoolNode( need_pf_cmp, BoolTest::ge ); + Node* need_pf_bol = new BoolNode(need_pf_cmp, BoolTest::ge); transform_later(need_pf_bol); - IfNode *need_pf_iff = new IfNode( needgc_false, need_pf_bol, - PROB_UNLIKELY_MAG(4), COUNT_UNKNOWN ); + IfNode* need_pf_iff = new IfNode(needgc_false, need_pf_bol, + PROB_UNLIKELY_MAG(4), COUNT_UNKNOWN); transform_later(need_pf_iff); // true node, add prefetchdistance - Node *need_pf_true = new IfTrueNode( need_pf_iff ); + Node* need_pf_true = new IfTrueNode(need_pf_iff); transform_later(need_pf_true); - Node *need_pf_false = new IfFalseNode( need_pf_iff ); + Node* need_pf_false = new IfFalseNode(need_pf_iff); transform_later(need_pf_false); - Node *new_pf_wmt = new AddPNode( top(), old_pf_wm, - _igvn.MakeConX(AllocatePrefetchDistance) ); - transform_later(new_pf_wmt ); + Node* new_pf_wmt = AddPNode::make_off_heap(old_pf_wm, + _igvn.MakeConX(AllocatePrefetchDistance)); + transform_later(new_pf_wmt); new_pf_wmt->set_req(0, need_pf_true); - Node *store_new_wmt = new StorePNode(need_pf_true, + Node* store_new_wmt = new StorePNode(need_pf_true, contended_phi_rawmem, eden_pf_adr, TypeRawPtr::BOTTOM, new_pf_wmt, MemNode::unordered); transform_later(store_new_wmt); // adding prefetches - pf_phi_abio->init_req( fall_in_path, i_o ); + pf_phi_abio->init_req(fall_in_path, i_o); - Node *prefetch_adr; - Node *prefetch; + Node* prefetch_adr; + Node* prefetch; uint step_size = AllocatePrefetchStepSize; uint distance = 0; - for ( intx i = 0; i < lines; i++ ) { - prefetch_adr = new AddPNode( old_pf_wm, new_pf_wmt, - _igvn.MakeConX(distance) ); + for (intx i = 0; i < lines; i++) { + prefetch_adr = AddPNode::make_off_heap(new_pf_wmt, + _igvn.MakeConX(distance)); transform_later(prefetch_adr); - prefetch = new PrefetchAllocationNode( i_o, prefetch_adr ); + prefetch = new PrefetchAllocationNode(i_o, prefetch_adr); transform_later(prefetch); distance += step_size; i_o = prefetch; } - pf_phi_abio->set_req( pf_path, i_o ); + pf_phi_abio->set_req(pf_path, i_o); - pf_region->init_req( fall_in_path, need_pf_false ); - pf_region->init_req( pf_path, need_pf_true ); + pf_region->init_req(fall_in_path, need_pf_false); + pf_region->init_req(pf_path, need_pf_true); - pf_phi_rawmem->init_req( fall_in_path, contended_phi_rawmem ); - pf_phi_rawmem->init_req( pf_path, store_new_wmt ); + pf_phi_rawmem->init_req(fall_in_path, contended_phi_rawmem); + pf_phi_rawmem->init_req(pf_path, store_new_wmt); transform_later(pf_region); transform_later(pf_phi_rawmem); @@ -1901,7 +1903,7 @@ Node* PhaseMacroExpand::prefetch_allocation(Node* i_o, Node*& needgc_false, needgc_false = pf_region; contended_phi_rawmem = pf_phi_rawmem; i_o = pf_phi_abio; - } else if( UseTLAB && AllocatePrefetchStyle == 3 ) { + } else if (UseTLAB && AllocatePrefetchStyle == 3) { // Insert a prefetch instruction for each allocation. // This code is used to generate 1 prefetch instruction per cache line. @@ -1910,13 +1912,12 @@ Node* PhaseMacroExpand::prefetch_allocation(Node* i_o, Node*& needgc_false, uint distance = AllocatePrefetchDistance; // Next cache address. - Node *cache_adr = new AddPNode(old_eden_top, old_eden_top, - _igvn.MakeConX(step_size + distance)); + Node* cache_adr = AddPNode::make_off_heap(old_eden_top, + _igvn.MakeConX(step_size + distance)); transform_later(cache_adr); cache_adr = new CastP2XNode(needgc_false, cache_adr); transform_later(cache_adr); - // Address is aligned to execute prefetch to the beginning of cache line size - // (it is important when BIS instruction is used on SPARC as prefetch). + // Address is aligned to execute prefetch to the beginning of cache line size. Node* mask = _igvn.MakeConX(~(intptr_t)(step_size-1)); cache_adr = new AndXNode(cache_adr, mask); transform_later(cache_adr); @@ -1924,36 +1925,36 @@ Node* PhaseMacroExpand::prefetch_allocation(Node* i_o, Node*& needgc_false, transform_later(cache_adr); // Prefetch - Node *prefetch = new PrefetchAllocationNode( contended_phi_rawmem, cache_adr ); + Node* prefetch = new PrefetchAllocationNode(contended_phi_rawmem, cache_adr); prefetch->set_req(0, needgc_false); transform_later(prefetch); contended_phi_rawmem = prefetch; - Node *prefetch_adr; + Node* prefetch_adr; distance = step_size; - for ( intx i = 1; i < lines; i++ ) { - prefetch_adr = new AddPNode( cache_adr, cache_adr, - _igvn.MakeConX(distance) ); + for (intx i = 1; i < lines; i++) { + prefetch_adr = AddPNode::make_off_heap(cache_adr, + _igvn.MakeConX(distance)); transform_later(prefetch_adr); - prefetch = new PrefetchAllocationNode( contended_phi_rawmem, prefetch_adr ); + prefetch = new PrefetchAllocationNode(contended_phi_rawmem, prefetch_adr); transform_later(prefetch); distance += step_size; contended_phi_rawmem = prefetch; } - } else if( AllocatePrefetchStyle > 0 ) { + } else if (AllocatePrefetchStyle > 0) { // Insert a prefetch for each allocation only on the fast-path - Node *prefetch_adr; - Node *prefetch; + Node* prefetch_adr; + Node* prefetch; // Generate several prefetch instructions. uint step_size = AllocatePrefetchStepSize; uint distance = AllocatePrefetchDistance; - for ( intx i = 0; i < lines; i++ ) { - prefetch_adr = new AddPNode( top(), new_eden_top, - _igvn.MakeConX(distance) ); + for (intx i = 0; i < lines; i++) { + prefetch_adr = AddPNode::make_off_heap(new_eden_top, + _igvn.MakeConX(distance)); transform_later(prefetch_adr); - prefetch = new PrefetchAllocationNode( i_o, prefetch_adr ); + prefetch = new PrefetchAllocationNode(i_o, prefetch_adr); // Do not let it float too high, since if eden_top == eden_end, // both might be null. - if( i == 0 ) { // Set control for first prefetch, next follows it + if (i == 0) { // Set control for first prefetch, next follows it prefetch->init_req(0, needgc_false); } transform_later(prefetch); diff --git a/src/hotspot/share/opto/macro.hpp b/src/hotspot/share/opto/macro.hpp index 0f8d8fb5172..59a455cae6d 100644 --- a/src/hotspot/share/opto/macro.hpp +++ b/src/hotspot/share/opto/macro.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -40,19 +40,32 @@ private: public: // Helper methods roughly modeled after GraphKit: - Node* basic_plus_adr(Node* base, int offset) { - return (offset == 0)? base: basic_plus_adr(base, MakeConX(offset)); + Node* basic_plus_adr(Node* ptr, int offset, bool raw_base = false) { + return basic_plus_adr(ptr, MakeConX(offset), raw_base); } + Node* basic_plus_adr(Node* base, Node* ptr, int offset) { - return (offset == 0)? ptr: basic_plus_adr(base, ptr, MakeConX(offset)); + return basic_plus_adr(base, ptr, MakeConX(offset)); } - Node* basic_plus_adr(Node* base, Node* offset) { - return basic_plus_adr(base, base, offset); + + Node* basic_plus_adr(Node* ptr, Node* offset, bool raw_base = false) { + Node* base = raw_base ? top() : ptr; + return basic_plus_adr(base, ptr, offset); } + Node* basic_plus_adr(Node* base, Node* ptr, Node* offset) { - Node* adr = new AddPNode(base, ptr, offset); - return transform_later(adr); + return (offset == MakeConX(0)) ? + ptr : transform_later(AddPNode::make_with_base(base, ptr, offset)); } + + Node* off_heap_plus_addr(Node* ptr, int offset) { + return basic_plus_adr(top(), ptr, MakeConX(offset)); + } + + Node* off_heap_plus_addr(Node* ptr, Node* offset) { + return basic_plus_adr(top(), ptr, offset); + } + Node* transform_later(Node* n) { // equivalent to _gvn.transform in GraphKit, Ideal, etc. _igvn.register_new_node_with_optimizer(n); @@ -109,7 +122,7 @@ private: // More helper methods modeled after GraphKit for array copy void insert_mem_bar(Node** ctrl, Node** mem, int opcode, int alias_idx, Node* precedent = nullptr); - Node* array_element_address(Node* ary, Node* idx, BasicType elembt); + Node* array_element_address(Node* ary, Node* idx, BasicType elembt, bool raw_base); Node* ConvI2L(Node* offset); // helper methods modeled after LibraryCallKit for array copy diff --git a/src/hotspot/share/opto/macroArrayCopy.cpp b/src/hotspot/share/opto/macroArrayCopy.cpp index 0719ffc45a5..139775506db 100644 --- a/src/hotspot/share/opto/macroArrayCopy.cpp +++ b/src/hotspot/share/opto/macroArrayCopy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, 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 @@ -55,10 +55,10 @@ void PhaseMacroExpand::insert_mem_bar(Node** ctrl, Node** mem, int opcode, int a } } -Node* PhaseMacroExpand::array_element_address(Node* ary, Node* idx, BasicType elembt) { +Node* PhaseMacroExpand::array_element_address(Node* ary, Node* idx, BasicType elembt, bool raw_base) { uint shift = exact_log2(type2aelembytes(elembt)); uint header = arrayOopDesc::base_offset_in_bytes(elembt); - Node* base = basic_plus_adr(ary, header); + Node* base = basic_plus_adr(ary, header, raw_base); #ifdef _LP64 // see comment in GraphKit::array_element_address int index_max = max_jint - 1; // array size is max_jint, index is one less @@ -67,7 +67,7 @@ Node* PhaseMacroExpand::array_element_address(Node* ary, Node* idx, BasicType el #endif Node* scale = new LShiftXNode(idx, intcon(shift)); transform_later(scale); - return basic_plus_adr(ary, base, scale); + return basic_plus_adr(raw_base ? top() : ary, base, scale); } Node* PhaseMacroExpand::ConvI2L(Node* offset) { @@ -379,6 +379,7 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode* bool disjoint_bases, bool length_never_negative, RegionNode* slow_region) { + Node* orig_dest = dest; if (slow_region == nullptr) { slow_region = new RegionNode(1); transform_later(slow_region); @@ -411,6 +412,7 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode* assert(dest->is_CheckCastPP(), "sanity"); assert(dest->in(0)->in(0) == init, "dest pinned"); adr_type = TypeRawPtr::BOTTOM; // all initializations are into raw memory + dest = dest->in(1); // writing to raw memory requires a raw base // From this point on, every exit path is responsible for // initializing any non-copied parts of the object to zero. // Also, if this flag is set we make sure that arraycopy interacts properly @@ -638,7 +640,7 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode* // (At this point we can assume disjoint_bases, since types differ.) int ek_offset = in_bytes(ObjArrayKlass::element_klass_offset()); - Node* p1 = basic_plus_adr(top(), dest_klass, ek_offset); + Node* p1 = off_heap_plus_addr(dest_klass, ek_offset); Node* n1 = LoadKlassNode::make(_igvn, C->immutable_memory(), p1, TypeRawPtr::BOTTOM); Node* dest_elem_klass = transform_later(n1); Node* cv = generate_checkcast_arraycopy(&local_ctrl, &local_mem, @@ -771,7 +773,7 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode* local_mem = generate_slow_arraycopy(ac, &local_ctrl, local_mem, &local_io, adr_type, - src, src_offset, dest, dest_offset, + src, src_offset, orig_dest, dest_offset, copy_length, /*dest_uninitialized*/false); result_region->init_req(slow_call_path, local_ctrl); @@ -839,7 +841,7 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode* _igvn.replace_node(_callprojs.fallthrough_catchproj, *ctrl); #ifdef ASSERT - const TypeOopPtr* dest_t = _igvn.type(dest)->is_oopptr(); + const TypeOopPtr* dest_t = _igvn.type(orig_dest)->is_oopptr(); if (dest_t->is_known_instance()) { ArrayCopyNode* ac = nullptr; assert(ArrayCopyNode::may_modify(dest_t, (*ctrl)->in(0)->as_MemBar(), &_igvn, ac), "dependency on arraycopy lost"); @@ -915,12 +917,12 @@ void PhaseMacroExpand::generate_clear_array(Node* ctrl, MergeMemNode* merge_mem, if (start_con >= 0 && end_con >= 0) { // Constant start and end. Simple. mem = ClearArrayNode::clear_memory(ctrl, mem, dest, - start_con, end_con, false, &_igvn); + start_con, end_con, adr_type == TypeRawPtr::BOTTOM, &_igvn); } else if (start_con >= 0 && dest_size != top()) { // Constant start, pre-rounded end after the tail of the array. Node* end = dest_size; mem = ClearArrayNode::clear_memory(ctrl, mem, dest, - start_con, end, false, &_igvn); + start_con, end, adr_type == TypeRawPtr::BOTTOM, &_igvn); } else if (start_con >= 0 && slice_len != top()) { // Constant start, non-constant end. End needs rounding up. // End offset = round_up(abase + ((slice_idx_con + slice_len) << scale), 8) @@ -933,7 +935,7 @@ void PhaseMacroExpand::generate_clear_array(Node* ctrl, MergeMemNode* merge_mem, end = transform_later(new AddXNode(end, MakeConX(end_base)) ); end = transform_later(new AndXNode(end, MakeConX(~end_round)) ); mem = ClearArrayNode::clear_memory(ctrl, mem, dest, - start_con, end, false, &_igvn); + start_con, end, adr_type == TypeRawPtr::BOTTOM, &_igvn); } else if (start_con < 0 && dest_size != top()) { // Non-constant start, pre-rounded end after the tail of the array. // This is almost certainly a "round-to-end" operation. @@ -960,14 +962,14 @@ void PhaseMacroExpand::generate_clear_array(Node* ctrl, MergeMemNode* merge_mem, if (bump_bit != 0) { // Store a zero to the immediately preceding jint: Node* x1 = transform_later(new AddXNode(start, MakeConX(-bump_bit)) ); - Node* p1 = basic_plus_adr(dest, x1); + Node* p1 = basic_plus_adr(dest, x1, adr_type == TypeRawPtr::BOTTOM); mem = StoreNode::make(_igvn, ctrl, mem, p1, adr_type, intcon(0), T_INT, MemNode::unordered); mem = transform_later(mem); } } Node* end = dest_size; // pre-rounded mem = ClearArrayNode::clear_memory(ctrl, mem, dest, - start, end, false, &_igvn); + start, end, adr_type == TypeRawPtr::BOTTOM, &_igvn); } else { // Non-constant start, unrounded non-constant end. // (Nobody zeroes a random midsection of an array using this routine.) @@ -1009,7 +1011,7 @@ bool PhaseMacroExpand::generate_block_arraycopy(Node** ctrl, MergeMemNode** mem, if (((src_off | dest_off) & (BytesPerLong-1)) == BytesPerInt && ((src_off ^ dest_off) & (BytesPerLong-1)) == 0) { Node* sptr = basic_plus_adr(src, src_off); - Node* dptr = basic_plus_adr(dest, dest_off); + Node* dptr = basic_plus_adr(dest, dest_off, adr_type == TypeRawPtr::BOTTOM); const TypePtr* s_adr_type = _igvn.type(sptr)->is_ptr(); assert(s_adr_type->isa_aryptr(), "impossible slice"); uint s_alias_idx = C->get_alias_index(s_adr_type); @@ -1037,7 +1039,7 @@ bool PhaseMacroExpand::generate_block_arraycopy(Node** ctrl, MergeMemNode** mem, // Do this copy by giant steps. Node* sptr = basic_plus_adr(src, src_off); - Node* dptr = basic_plus_adr(dest, dest_off); + Node* dptr = basic_plus_adr(dest, dest_off, adr_type == TypeRawPtr::BOTTOM); Node* countx = dest_size; countx = transform_later(new SubXNode(countx, MakeConX(dest_off))); countx = transform_later(new URShiftXNode(countx, intcon(LogBytesPerLong))); @@ -1129,13 +1131,13 @@ Node* PhaseMacroExpand::generate_checkcast_arraycopy(Node** ctrl, MergeMemNode** // look in each non-null element's class, at the desired klass's // super_check_offset, for the desired klass. int sco_offset = in_bytes(Klass::super_check_offset_offset()); - Node* p3 = basic_plus_adr(top(), dest_elem_klass, sco_offset); + Node* p3 = off_heap_plus_addr(dest_elem_klass, sco_offset); Node* n3 = new LoadINode(nullptr, *mem /*memory(p3)*/, p3, _igvn.type(p3)->is_ptr(), TypeInt::INT, MemNode::unordered); Node* check_offset = ConvI2X(transform_later(n3)); Node* check_value = dest_elem_klass; - Node* src_start = array_element_address(src, src_offset, T_OBJECT); - Node* dest_start = array_element_address(dest, dest_offset, T_OBJECT); + Node* src_start = array_element_address(src, src_offset, T_OBJECT, false); + Node* dest_start = array_element_address(dest, dest_offset, T_OBJECT, adr_type == TypeRawPtr::BOTTOM); const TypeFunc* call_type = OptoRuntime::checkcast_arraycopy_Type(); Node* call = make_leaf_call(*ctrl, *mem, call_type, copyfunc_addr, "checkcast_arraycopy", adr_type, @@ -1190,8 +1192,8 @@ void PhaseMacroExpand::generate_unchecked_arraycopy(Node** ctrl, MergeMemNode** Node* src_start = src; Node* dest_start = dest; if (src_offset != nullptr || dest_offset != nullptr) { - src_start = array_element_address(src, src_offset, basic_elem_type); - dest_start = array_element_address(dest, dest_offset, basic_elem_type); + src_start = array_element_address(src, src_offset, basic_elem_type, false); + dest_start = array_element_address(dest, dest_offset, basic_elem_type, adr_type == TypeRawPtr::BOTTOM); } // Figure out which arraycopy runtime method to call. diff --git a/src/hotspot/share/opto/matcher.hpp b/src/hotspot/share/opto/matcher.hpp index 69e5ab354f1..4de41d6f2ef 100644 --- a/src/hotspot/share/opto/matcher.hpp +++ b/src/hotspot/share/opto/matcher.hpp @@ -429,10 +429,6 @@ public: // Register for MODL projection of divmodL static const RegMask& modL_proj_mask(); - // Use hardware DIV instruction when it is faster than - // a code which use multiply for division by constant. - static bool use_asm_for_ldiv_by_con( jlong divisor ); - // Java-Interpreter calling convention // (what you use when calling between compiled-Java and Interpreted-Java diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index f7da9a96d34..3cd553e4bd1 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -1010,6 +1010,7 @@ bool LoadNode::is_immutable_value(Node* adr) { Node* LoadNode::make(PhaseGVN& gvn, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, const Type* rt, BasicType bt, MemOrd mo, ControlDependency control_dependency, bool require_atomic_access, bool unaligned, bool mismatched, bool unsafe, uint8_t barrier_data) { Compile* C = gvn.C; + assert(adr->is_top() || C->get_alias_index(gvn.type(adr)->is_ptr()) == C->get_alias_index(adr_type), "adr and adr_type must agree"); // sanity check the alias category against the created node type assert(!(adr_type->isa_oopptr() && @@ -1411,8 +1412,12 @@ Node* LoadNode::convert_to_unsigned_load(PhaseGVN& gvn) { assert(false, "no unsigned variant: %s", Name()); return nullptr; } + const Type* mem_t = gvn.type(in(MemNode::Address)); + if (mem_t == Type::TOP) { + return gvn.C->top(); + } return LoadNode::make(gvn, in(MemNode::Control), in(MemNode::Memory), in(MemNode::Address), - raw_adr_type(), rt, bt, _mo, _control_dependency, + mem_t->is_ptr(), rt, bt, _mo, _control_dependency, false /*require_atomic_access*/, is_unaligned_access(), is_mismatched_access()); } @@ -1431,8 +1436,12 @@ Node* LoadNode::convert_to_signed_load(PhaseGVN& gvn) { assert(false, "no signed variant: %s", Name()); return nullptr; } + const Type* mem_t = gvn.type(in(MemNode::Address)); + if (mem_t == Type::TOP) { + return gvn.C->top(); + } return LoadNode::make(gvn, in(MemNode::Control), in(MemNode::Memory), in(MemNode::Address), - raw_adr_type(), rt, bt, _mo, _control_dependency, + mem_t->is_ptr(), rt, bt, _mo, _control_dependency, false /*require_atomic_access*/, is_unaligned_access(), is_mismatched_access()); } @@ -1459,8 +1468,12 @@ Node* LoadNode::convert_to_reinterpret_load(PhaseGVN& gvn, const Type* rt) { const int op = Opcode(); bool require_atomic_access = (op == Op_LoadL && ((LoadLNode*)this)->require_atomic_access()) || (op == Op_LoadD && ((LoadDNode*)this)->require_atomic_access()); + const Type* mem_t = gvn.type(in(MemNode::Address)); + if (mem_t == Type::TOP) { + return gvn.C->top(); + } return LoadNode::make(gvn, in(MemNode::Control), in(MemNode::Memory), in(MemNode::Address), - raw_adr_type(), rt, bt, _mo, _control_dependency, + mem_t->is_ptr(), rt, bt, _mo, _control_dependency, require_atomic_access, is_unaligned_access(), is_mismatched); } @@ -1482,8 +1495,12 @@ Node* StoreNode::convert_to_reinterpret_store(PhaseGVN& gvn, Node* val, const Ty const int op = Opcode(); bool require_atomic_access = (op == Op_StoreL && ((StoreLNode*)this)->require_atomic_access()) || (op == Op_StoreD && ((StoreDNode*)this)->require_atomic_access()); + const Type* mem_t = gvn.type(in(MemNode::Address)); + if (mem_t == Type::TOP) { + return gvn.C->top(); + } StoreNode* st = StoreNode::make(gvn, in(MemNode::Control), in(MemNode::Memory), in(MemNode::Address), - raw_adr_type(), val, bt, _mo, require_atomic_access); + mem_t->is_ptr(), val, bt, _mo, require_atomic_access); bool is_mismatched = is_mismatched_access(); const TypeRawPtr* raw_type = gvn.type(in(MemNode::Memory))->isa_rawptr(); @@ -1829,7 +1846,7 @@ Node* LoadNode::split_through_phi(PhaseGVN* phase, bool ignore_missing_instance_ } if (base_is_phi && (base->in(0) == region)) { Node* base_x = base->in(i); // Clone address for loads from boxed objects. - Node* adr_x = phase->transform(new AddPNode(base_x,base_x,address->in(AddPNode::Offset))); + Node* adr_x = phase->transform(AddPNode::make_with_base(base_x, address->in(AddPNode::Offset))); x->set_req(Address, adr_x); } } @@ -2778,6 +2795,7 @@ Node* LoadRangeNode::Identity(PhaseGVN* phase) { StoreNode* StoreNode::make(PhaseGVN& gvn, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, Node* val, BasicType bt, MemOrd mo, bool require_atomic_access) { assert((mo == unordered || mo == release), "unexpected"); Compile* C = gvn.C; + assert(adr_type == nullptr || adr->is_top() || C->get_alias_index(gvn.type(adr)->is_ptr()) == C->get_alias_index(adr_type), "adr and adr_type must agree"); assert(C->get_alias_index(adr_type) != Compile::AliasIdxRaw || ctl != nullptr, "raw memory operations should have control edge"); @@ -4173,10 +4191,10 @@ Node *ClearArrayNode::Ideal(PhaseGVN *phase, bool can_reshape) { Node *off = phase->MakeConX(BytesPerLong); mem = new StoreLNode(in(0),mem,adr,atp,zero,MemNode::unordered,false); count--; - while( count-- ) { + while (count--) { mem = phase->transform(mem); - adr = phase->transform(new AddPNode(base,adr,off)); - mem = new StoreLNode(in(0),mem,adr,atp,zero,MemNode::unordered,false); + adr = phase->transform(AddPNode::make_with_base(base, adr, off)); + mem = new StoreLNode(in(0), mem, adr, atp, zero, MemNode::unordered, false); } return mem; } @@ -4213,7 +4231,7 @@ Node* ClearArrayNode::make_address(Node* dest, Node* offset, bool raw_base, Phas // May be called as part of the initialization of a just allocated object base = phase->C->top(); } - return phase->transform(new AddPNode(base, dest, offset)); + return phase->transform(AddPNode::make_with_base(base, dest, offset)); } //----------------------------clear_memory------------------------------------- @@ -4331,7 +4349,9 @@ MemBarNode* MemBarNode::make(Compile* C, int opcode, int atp, Node* pn) { case Op_StoreStoreFence: return new StoreStoreFenceNode(C, atp, pn); case Op_MemBarAcquireLock: return new MemBarAcquireLockNode(C, atp, pn); case Op_MemBarReleaseLock: return new MemBarReleaseLockNode(C, atp, pn); + case Op_MemBarStoreLoad: return new MemBarStoreLoadNode(C, atp, pn); case Op_MemBarVolatile: return new MemBarVolatileNode(C, atp, pn); + case Op_MemBarFull: return new MemBarFullNode(C, atp, pn); case Op_MemBarCPUOrder: return new MemBarCPUOrderNode(C, atp, pn); case Op_OnSpinWait: return new OnSpinWaitNode(C, atp, pn); case Op_Initialize: return new InitializeNode(C, atp, pn); @@ -5022,8 +5042,7 @@ Node* InitializeNode::make_raw_address(intptr_t offset, Node* addr = in(RawAddress); if (offset != 0) { Compile* C = phase->C; - addr = phase->transform( new AddPNode(C->top(), addr, - phase->MakeConX(offset)) ); + addr = phase->transform(AddPNode::make_off_heap(addr, phase->MakeConX(offset))); } return addr; } @@ -5070,7 +5089,7 @@ Node* InitializeNode::capture_store(StoreNode* st, intptr_t start, else ins_req(i, C->top()); // build a new edge } - Node* new_st = st->clone(); + Node* new_st = st->clone_with_adr_type(TypeRawPtr::BOTTOM); BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); new_st->set_req(MemNode::Control, in(Control)); new_st->set_req(MemNode::Memory, prev_mem); diff --git a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp index 2f26c67f0a8..7fa238f574d 100644 --- a/src/hotspot/share/opto/memnode.hpp +++ b/src/hotspot/share/opto/memnode.hpp @@ -173,6 +173,14 @@ public: static void dump_adr_type(const TypePtr* adr_type, outputStream* st); virtual void dump_spec(outputStream *st) const; #endif + + MemNode* clone_with_adr_type(const TypePtr* adr_type) const { + MemNode* new_node = clone()->as_Mem(); +#ifdef ASSERT + new_node->_adr_type = adr_type; +#endif + return new_node; + } }; // Analyze a MemNode to try to prove that it is independent from other memory accesses @@ -1313,6 +1321,13 @@ public: virtual int Opcode() const; }; +class MemBarStoreLoadNode : public MemBarNode { +public: + MemBarStoreLoadNode(Compile* C, int alias_idx, Node* precedent) + : MemBarNode(C, alias_idx, precedent) {} + virtual int Opcode() const; +}; + // Ordering between a volatile store and a following volatile load. // Requires multi-CPU visibility? class MemBarVolatileNode: public MemBarNode { @@ -1322,6 +1337,14 @@ public: virtual int Opcode() const; }; +// A full barrier blocks all loads and stores from moving across it +class MemBarFullNode : public MemBarNode { +public: + MemBarFullNode(Compile* C, int alias_idx, Node* precedent) + : MemBarNode(C, alias_idx, precedent) {} + virtual int Opcode() const; +}; + // Ordering within the same CPU. Used to order unsafe memory references // inside the compiler when we lack alias info. Not needed "outside" the // compiler because the CPU does all the ordering for us. diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index cac9f1dcc37..d7022b5f7ec 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -651,31 +651,19 @@ Node* AndINode::Identity(PhaseGVN* phase) { return in(1); } - Node* in1 = in(1); - uint op = in1->Opcode(); - const TypeInt* t2 = phase->type(in(2))->isa_int(); - if (t2 && t2->is_con()) { - int con = t2->get_con(); - // Masking off high bits which are always zero is useless. - const TypeInt* t1 = phase->type(in(1))->isa_int(); - if (t1 != nullptr && t1->_lo >= 0) { - jint t1_support = right_n_bits(1 + log2i_graceful(t1->_hi)); - if ((t1_support & con) == t1_support) - return in1; - } - // Masking off the high bits of a unsigned-shift-right is not - // needed either. - if (op == Op_URShiftI) { - const TypeInt* t12 = phase->type(in1->in(2))->isa_int(); - if (t12 && t12->is_con()) { // Shift is by a constant - int shift = t12->get_con(); - shift &= BitsPerJavaInteger - 1; // semantics of Java shifts - int mask = max_juint >> shift; - if ((mask & con) == mask) // If AND is useless, skip it - return in1; - } - } + const TypeInt* t1 = phase->type(in(1))->is_int(); + const TypeInt* t2 = phase->type(in(2))->is_int(); + + if ((~t1->_bits._ones & ~t2->_bits._zeros) == 0) { + // All bits that might be 0 in in1 are known to be 0 in in2 + return in(2); } + + if ((~t2->_bits._ones & ~t1->_bits._zeros) == 0) { + // All bits that might be 0 in in2 are known to be 0 in in1 + return in(1); + } + return MulNode::Identity(phase); } @@ -779,32 +767,19 @@ Node* AndLNode::Identity(PhaseGVN* phase) { return in(1); } - Node *usr = in(1); - const TypeLong *t2 = phase->type( in(2) )->isa_long(); - if( t2 && t2->is_con() ) { - jlong con = t2->get_con(); - // Masking off high bits which are always zero is useless. - const TypeLong* t1 = phase->type( in(1) )->isa_long(); - if (t1 != nullptr && t1->_lo >= 0) { - int bit_count = log2i_graceful(t1->_hi) + 1; - jlong t1_support = jlong(max_julong >> (BitsPerJavaLong - bit_count)); - if ((t1_support & con) == t1_support) - return usr; - } - uint lop = usr->Opcode(); - // Masking off the high bits of a unsigned-shift-right is not - // needed either. - if( lop == Op_URShiftL ) { - const TypeInt *t12 = phase->type( usr->in(2) )->isa_int(); - if( t12 && t12->is_con() ) { // Shift is by a constant - int shift = t12->get_con(); - shift &= BitsPerJavaLong - 1; // semantics of Java shifts - jlong mask = max_julong >> shift; - if( (mask&con) == mask ) // If AND is useless, skip it - return usr; - } - } + const TypeLong* t1 = phase->type(in(1))->is_long(); + const TypeLong* t2 = phase->type(in(2))->is_long(); + + if ((~t1->_bits._ones & ~t2->_bits._zeros) == 0) { + // All bits that might be 0 in in1 are known to be 0 in in2 + return in(2); } + + if ((~t2->_bits._ones & ~t1->_bits._zeros) == 0) { + // All bits that might be 0 in in2 are known to be 0 in in1 + return in(1); + } + return MulNode::Identity(phase); } @@ -1143,21 +1118,24 @@ const Type* LShiftNode::ValueIL(PhaseGVN* phase, BasicType bt) const { return t1; } - // Either input is BOTTOM ==> the result is BOTTOM - if ((t1 == TypeInteger::bottom(bt)) || (t2 == TypeInt::INT) || - (t1 == Type::BOTTOM) || (t2 == Type::BOTTOM)) { + // If nothing is known about the shift amount then the result is BOTTOM + if (t2 == TypeInt::INT) { return TypeInteger::bottom(bt); } const TypeInteger* r1 = t1->is_integer(bt); // Handy access - const TypeInt* r2 = t2->is_int(); // Handy access + // Since the shift semantics in Java take into account only the bottom five + // bits for ints and the bottom six bits for longs, we can further constrain + // the range of values of the shift amount by ANDing with the right mask based + // on whether the type is int or long. + const TypeInt* mask = TypeInt::make(bits_per_java_integer(bt) - 1); + const TypeInt* r2 = RangeInference::infer_and(t2->is_int(), mask); if (!r2->is_con()) { return TypeInteger::bottom(bt); } uint shift = r2->get_con(); - shift &= bits_per_java_integer(bt) - 1; // semantics of Java shifts // Shift by a multiple of 32/64 does nothing: if (shift == 0) { return t1; @@ -1166,22 +1144,20 @@ const Type* LShiftNode::ValueIL(PhaseGVN* phase, BasicType bt) const { // If the shift is a constant, shift the bounds of the type, // unless this could lead to an overflow. if (!r1->is_con()) { - jlong lo = r1->lo_as_long(), hi = r1->hi_as_long(); #ifdef ASSERT if (bt == T_INT) { + jlong lo = r1->lo_as_long(), hi = r1->hi_as_long(); jint lo_int = r1->is_int()->_lo, hi_int = r1->is_int()->_hi; assert((java_shift_right(java_shift_left(lo, shift, bt), shift, bt) == lo) == (((lo_int << shift) >> shift) == lo_int), "inconsistent"); assert((java_shift_right(java_shift_left(hi, shift, bt), shift, bt) == hi) == (((hi_int << shift) >> shift) == hi_int), "inconsistent"); } #endif - if (java_shift_right(java_shift_left(lo, shift, bt), shift, bt) == lo && - java_shift_right(java_shift_left(hi, shift, bt), shift, bt) == hi) { - // No overflow. The range shifts up cleanly. - return TypeInteger::make(java_shift_left(lo, shift, bt), - java_shift_left(hi, shift, bt), - MAX2(r1->_widen, r2->_widen), bt); + + if (bt == T_INT) { + return RangeInference::infer_lshift(r1->is_int(), shift); } - return TypeInteger::bottom(bt); + + return RangeInference::infer_lshift(r1->is_long(), shift); } return TypeInteger::make(java_shift_left(r1->get_con_as_long(bt), shift, bt), bt); diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index 136fc8ac864..a70620fac5b 100644 --- a/src/hotspot/share/opto/output.cpp +++ b/src/hotspot/share/opto/output.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -363,15 +363,6 @@ bool PhaseOutput::need_stack_bang(int frame_size_in_bytes) const { DEBUG_ONLY(|| true))); } -bool PhaseOutput::need_register_stack_bang() const { - // Determine if we need to generate a register stack overflow check. - // This is only used on architectures which have split register - // and memory stacks. - // Bang if the method is not a stub function and has java calls - return (C->stub_function() == nullptr && C->has_java_calls()); -} - - // Compute the size of first NumberOfLoopInstrToAlign instructions at the top // of a loop. When aligning a loop we need to provide enough instructions // in cpu's fetch buffer to feed decoders. The loop alignment could be @@ -2923,17 +2914,30 @@ void Scheduling::ComputeRegisterAntidependencies(Block *b) { Node *m = b->get_node(i); - // Add precedence edge from following safepoint to use of derived pointer - if( last_safept_node != end_node && + if (last_safept_node != end_node && m != last_safept_node) { + bool need_safept_prec = false; + // Add precedence edge from following safepoint to use of derived pointer for (uint k = 1; k < m->req(); k++) { const Type *t = m->in(k)->bottom_type(); - if( t->isa_oop_ptr() && - t->is_ptr()->offset() != 0 ) { - last_safept_node->add_prec( m ); + if (t->isa_oop_ptr() && + t->is_ptr()->offset() != 0) { + need_safept_prec = true; break; } } + // A CheckCastPP whose input is still RawPtr must stay above the following safepoint. + // Otherwise post-regalloc block-local scheduling can leave a live raw oop at the safepoint. + if (!need_safept_prec && m->is_Mach() && + m->as_Mach()->ideal_Opcode() == Op_CheckCastPP) { + Node* def = m->in(1); + if (def != nullptr && def->bottom_type()->base() == Type::RawPtr) { + need_safept_prec = true; + } + } + if (need_safept_prec) { + last_safept_node->add_prec(m); + } } if( n->jvms() ) { // Precedence edge from derived to safept diff --git a/src/hotspot/share/opto/output.hpp b/src/hotspot/share/opto/output.hpp index 432ad3638b2..5cca59ea0e4 100644 --- a/src/hotspot/share/opto/output.hpp +++ b/src/hotspot/share/opto/output.hpp @@ -109,7 +109,6 @@ public: // Convert Nodes to instruction bits and pass off to the VM void Output(); bool need_stack_bang(int frame_size_in_bytes) const; - bool need_register_stack_bang() const; void compute_loop_first_inst_sizes(); void install_code(ciMethod* target, diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index 2f699650037..647e8515b30 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -104,9 +104,9 @@ void Parse::print_statistics() { Node *Parse::fetch_interpreter_state(int index, BasicType bt, Node* local_addrs) { - Node *mem = memory(Compile::AliasIdxRaw); - Node *adr = basic_plus_adr(top(), local_addrs, -index*wordSize); - Node *ctl = control(); + Node* mem = memory(Compile::AliasIdxRaw); + Node* adr = off_heap_plus_addr(local_addrs, -index*wordSize); + Node* ctl = control(); // Very similar to LoadNode::make, except we handle un-aligned longs and // doubles on Sparc. Intel can handle them just fine directly. @@ -120,7 +120,7 @@ Node *Parse::fetch_interpreter_state(int index, case T_DOUBLE: { // Since arguments are in reverse order, the argument address 'adr' // refers to the back half of the long/double. Recompute adr. - adr = basic_plus_adr(top(), local_addrs, -(index+1)*wordSize); + adr = off_heap_plus_addr(local_addrs, -(index+1)*wordSize); if (Matcher::misaligned_doubles_ok) { l = (bt == T_DOUBLE) ? (Node*)new LoadDNode(ctl, mem, adr, TypeRawPtr::BOTTOM, Type::DOUBLE, MemNode::unordered) @@ -219,7 +219,7 @@ void Parse::load_interpreter_state(Node* osr_buf) { // Commute monitors from interpreter frame to compiler frame. assert(jvms()->monitor_depth() == 0, "should be no active locks at beginning of osr"); int mcnt = osr_block->flow()->monitor_count(); - Node *monitors_addr = basic_plus_adr(top(), osr_buf, (max_locals+mcnt*2-1)*wordSize); + Node* monitors_addr = off_heap_plus_addr(osr_buf, (max_locals+mcnt*2-1)*wordSize); for (index = 0; index < mcnt; index++) { // Make a BoxLockNode for the monitor. BoxLockNode* osr_box = new BoxLockNode(next_monitor()); @@ -270,7 +270,7 @@ void Parse::load_interpreter_state(Node* osr_buf) { } // Extract the needed locals from the interpreter frame. - Node *locals_addr = basic_plus_adr(top(), osr_buf, (max_locals-1)*wordSize); + Node* locals_addr = off_heap_plus_addr(osr_buf, (max_locals-1)*wordSize); // find all the locals that the interpreter thinks contain live oops const ResourceBitMap live_oops = method()->live_local_oops_at_bci(osr_bci()); @@ -2127,7 +2127,7 @@ void Parse::call_register_finalizer() { Node* klass_addr = basic_plus_adr( receiver, receiver, oopDesc::klass_offset_in_bytes() ); Node* klass = _gvn.transform(LoadKlassNode::make(_gvn, immutable_memory(), klass_addr, TypeInstPtr::KLASS)); - Node* access_flags_addr = basic_plus_adr(top(), klass, in_bytes(Klass::misc_flags_offset())); + Node* access_flags_addr = off_heap_plus_addr(klass, in_bytes(Klass::misc_flags_offset())); Node* access_flags = make_load(nullptr, access_flags_addr, TypeInt::UBYTE, T_BOOLEAN, MemNode::unordered); Node* mask = _gvn.transform(new AndINode(access_flags, intcon(KlassFlags::_misc_has_finalizer))); @@ -2273,9 +2273,9 @@ void Parse::add_safepoint() { sfpnt->init_req(TypeFunc::FramePtr , top() ); // Create a node for the polling address - Node *polladr; - Node *thread = _gvn.transform(new ThreadLocalNode()); - Node *polling_page_load_addr = _gvn.transform(basic_plus_adr(top(), thread, in_bytes(JavaThread::polling_page_offset()))); + Node* polladr; + Node* thread = _gvn.transform(new ThreadLocalNode()); + Node* polling_page_load_addr = _gvn.transform(off_heap_plus_addr(thread, in_bytes(JavaThread::polling_page_offset()))); polladr = make_load(control(), polling_page_load_addr, TypeRawPtr::BOTTOM, T_ADDRESS, MemNode::unordered); sfpnt->init_req(TypeFunc::Parms+0, _gvn.transform(polladr)); diff --git a/src/hotspot/share/opto/parseHelper.cpp b/src/hotspot/share/opto/parseHelper.cpp index 2e8fca68a8d..232f5d6c89a 100644 --- a/src/hotspot/share/opto/parseHelper.cpp +++ b/src/hotspot/share/opto/parseHelper.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -220,7 +220,7 @@ void Parse::array_store_check() { // Extract the array element class int element_klass_offset = in_bytes(ObjArrayKlass::element_klass_offset()); - Node* p2 = basic_plus_adr(top(), array_klass, element_klass_offset); + Node* p2 = off_heap_plus_addr(array_klass, element_klass_offset); Node* a_e_klass = _gvn.transform(LoadKlassNode::make(_gvn, immutable_memory(), p2, tak)); assert(array_klass->is_Con() == a_e_klass->is_Con() || StressReflectiveCode, "a constant array type must come with a constant element type"); diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index c77316e7d0b..29961e152b3 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2581,6 +2581,26 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ return u->Opcode() == Op_CmpU; }); } + // If changed AddI/AddL inputs, check URShift users for + // "((X << z) + Y) >>> z" optimization in URShift{I,L}Node::Ideal. + if (use_op == Op_AddI || use_op == Op_AddL) { + add_users_to_worklist_if(worklist, use, [](Node* u) { + return u->Opcode() == Op_URShiftI || u->Opcode() == Op_URShiftL; + }); + } + // If changed LShiftI/LShiftL inputs, check AddI/AddL users for their + // URShiftI/URShiftL users for "((x << z) + y) >>> z" optimization opportunity + // (see URShiftINode::Ideal). Handles the case where the LShift input changes. + if (use_op == Op_LShiftI || use_op == Op_LShiftL) { + for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { + Node* add = use->fast_out(i2); + if (add->Opcode() == Op_AddI || add->Opcode() == Op_AddL) { + add_users_to_worklist_if(worklist, add, [](Node* u) { + return u->Opcode() == Op_URShiftI || u->Opcode() == Op_URShiftL; + }); + } + } + } // If changed AndI/AndL inputs, check RShift/URShift users for "(x & mask) >> shift" optimization opportunity if (use_op == Op_AndI || use_op == Op_AndL) { add_users_to_worklist_if(worklist, use, [](Node* u) { diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index 66ea741a2da..7c0f12f6ef7 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -28,6 +28,7 @@ #include "cppstdlib/limits.hpp" #include "cppstdlib/type_traits.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/intn_t.hpp" class outputStream; class Type; @@ -407,6 +408,40 @@ public: return TypeIntMirror, U>::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}); }); } + + // Compute `known_bits` by shifting known bits of `t1` left and setting the + // low `shift` bits to zeros. Also update the signed and unsigned ranges when + // the shift operation does not cause an overflow. The caller is responsible + // for normalizing the shift amount (i.e. masking with 31 for ints or 63 for + // longs). + template + static CTP infer_lshift(CTP t1, int masked_shift) { + assert(masked_shift >= 0 && + masked_shift < HotSpotNumerics::type_width>(), + "shift is out of range"); + + U pattern = (U(1) << masked_shift) - U(1); + U known_one_bits = t1->_bits._ones << masked_shift; + U known_zero_bits = (t1->_bits._zeros << masked_shift) | pattern; + KnownBits> known_bits{known_zero_bits, known_one_bits}; + + S shifted_slo = S(U(t1->_lo) << masked_shift); + S shifted_shi = S(U(t1->_hi) << masked_shift); + bool s_overflow = (shifted_slo >> masked_shift) != t1->_lo || + (shifted_shi >> masked_shift) != t1->_hi; + S slo = s_overflow ? std::numeric_limits>::min() : shifted_slo; + S shi = s_overflow ? std::numeric_limits>::max() : shifted_shi; + + U shifted_ulo = t1->_ulo << masked_shift; + U shifted_uhi = t1->_uhi << masked_shift; + bool u_overflow = (shifted_ulo >> masked_shift) != t1->_ulo || + (shifted_uhi >> masked_shift) != t1->_uhi; + U ulo = u_overflow ? std::numeric_limits>::min() : shifted_ulo; + U uhi = u_overflow ? std::numeric_limits>::max() : shifted_uhi; + + TypeIntPrototype, U> proto{{slo, shi}, {ulo, uhi}, known_bits}; + return CT::make(proto, t1->_widen); + } }; #endif // SHARE_OPTO_RANGEINFERENCE_HPP diff --git a/src/hotspot/share/opto/split_if.cpp b/src/hotspot/share/opto/split_if.cpp index dff8bf86606..91595c57a10 100644 --- a/src/hotspot/share/opto/split_if.cpp +++ b/src/hotspot/share/opto/split_if.cpp @@ -38,6 +38,11 @@ RegionNode* PhaseIdealLoop::split_thru_region(Node* n, RegionNode* region) { assert(n->is_CFG(), ""); RegionNode* r = new RegionNode(region->req()); IdealLoopTree* loop = get_loop(n); +#ifndef PRODUCT + if (TraceSplitIf) { + tty->print_cr(" Splitting %d %s through %d %s", n->_idx, n->Name(), region->_idx, region->Name()); + } +#endif for (uint i = 1; i < region->req(); i++) { Node* x = n->clone(); Node* in0 = n->in(0); @@ -145,6 +150,11 @@ bool PhaseIdealLoop::split_up( Node *n, Node *blk1, Node *blk2 ) { } // Now actually split-up this guy. One copy per control path merging. +#ifndef PRODUCT + if (TraceSplitIf) { + tty->print_cr(" Splitting up: %d %s", n->_idx, n->Name()); + } +#endif Node *phi = PhiNode::make_blank(blk1, n); for( uint j = 1; j < blk1->req(); j++ ) { Node *x = n->clone(); @@ -185,6 +195,11 @@ bool PhaseIdealLoop::split_up( Node *n, Node *blk1, Node *blk2 ) { // AddP and CheckCastPP have the same obj input after split if. bool PhaseIdealLoop::clone_cmp_loadklass_down(Node* n, const Node* blk1, const Node* blk2) { if (n->Opcode() == Op_AddP && at_relevant_ctrl(n, blk1, blk2)) { +#ifndef PRODUCT + if (TraceSplitIf) { + tty->print_cr(" Cloning down (LoadKlass): %d %s", n->_idx, n->Name()); + } +#endif Node_List cmp_nodes; uint old = C->unique(); for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { @@ -301,6 +316,11 @@ bool PhaseIdealLoop::clone_cmp_down(Node* n, const Node* blk1, const Node* blk2) at_relevant_ctrl(cmov, blk1, blk2)))) { // Must clone down +#ifndef PRODUCT + if (TraceSplitIf) { + tty->print_cr(" Cloning down (Cmp): %d %s", n->_idx, n->Name()); + } +#endif if (!n->is_FastLock()) { // Clone down any block-local BoolNode uses of this CmpNode for (DUIterator i = n->outs(); n->has_out(i); i++) { @@ -401,6 +421,12 @@ void PhaseIdealLoop::clone_template_assertion_expression_down(Node* node) { return; } +#ifndef PRODUCT + if (TraceSplitIf) { + tty->print_cr(" Cloning down (Template Assertion Expression): %d %s", node->_idx, node->Name()); + } +#endif + TemplateAssertionExpressionNode template_assertion_expression_node(node); auto clone_expression = [&](IfNode* template_assertion_predicate) { OpaqueTemplateAssertionPredicateNode* opaque_node = @@ -459,6 +485,11 @@ Node *PhaseIdealLoop::spinup( Node *iff_dom, Node *new_false, Node *new_true, No Node *phi_post; if( prior_n == new_false || prior_n == new_true ) { phi_post = def->clone(); +#ifndef PRODUCT + if (TraceSplitIf) { + tty->print_cr(" Spinup: cloning def to sink: %d %s -> %d %s", def->_idx, def->Name(), phi_post->_idx, phi_post->Name()); + } +#endif phi_post->set_req(0, prior_n ); register_new_node(phi_post, prior_n); } else { @@ -472,6 +503,11 @@ Node *PhaseIdealLoop::spinup( Node *iff_dom, Node *new_false, Node *new_true, No } else { assert( def->is_Phi(), "" ); assert( prior_n->is_Region(), "must be a post-dominating merge point" ); +#ifndef PRODUCT + if (TraceSplitIf) { + tty->print_cr(" Spinup: creating new Phi for merge: %d %s", def->_idx, def->Name()); + } +#endif // Need a Phi here phi_post = PhiNode::make_blank(prior_n, def); diff --git a/src/hotspot/share/opto/subtypenode.cpp b/src/hotspot/share/opto/subtypenode.cpp index 8e4c7d829a7..69b7058d053 100644 --- a/src/hotspot/share/opto/subtypenode.cpp +++ b/src/hotspot/share/opto/subtypenode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -182,7 +182,7 @@ bool SubTypeCheckNode::verify(PhaseGVN* phase) { return verify_helper(phase, load_klass(phase), cached_t); } case Compile::SSC_full_test: { - Node* p1 = phase->transform(new AddPNode(C->top(), superklass, phase->MakeConX(in_bytes(Klass::super_check_offset_offset())))); + Node* p1 = phase->transform(AddPNode::make_off_heap(superklass, phase->MakeConX(in_bytes(Klass::super_check_offset_offset())))); Node* chk_off = phase->transform(new LoadINode(nullptr, C->immutable_memory(), p1, phase->type(p1)->is_ptr(), TypeInt::INT, MemNode::unordered)); record_for_cleanup(chk_off, phase); @@ -194,7 +194,7 @@ bool SubTypeCheckNode::verify(PhaseGVN* phase) { #ifdef _LP64 chk_off_X = phase->transform(new ConvI2LNode(chk_off_X)); #endif - Node* p2 = phase->transform(new AddPNode(C->top(), subklass, chk_off_X)); + Node* p2 = phase->transform(AddPNode::make_off_heap(subklass, chk_off_X)); Node* nkls = phase->transform(LoadKlassNode::make(*phase, C->immutable_memory(), p2, phase->type(p2)->is_ptr(), TypeInstKlassPtr::OBJECT_OR_NULL)); return verify_helper(phase, nkls, cached_t); @@ -217,7 +217,7 @@ Node* SubTypeCheckNode::load_klass(PhaseGVN* phase) const { const Type* sub_t = phase->type(obj_or_subklass); Node* subklass = nullptr; if (sub_t->isa_oopptr()) { - Node* adr = phase->transform(new AddPNode(obj_or_subklass, obj_or_subklass, phase->MakeConX(oopDesc::klass_offset_in_bytes()))); + Node* adr = phase->transform(AddPNode::make_with_base(obj_or_subklass, phase->MakeConX(oopDesc::klass_offset_in_bytes()))); subklass = phase->transform(LoadKlassNode::make(*phase, phase->C->immutable_memory(), adr, TypeInstPtr::KLASS)); record_for_cleanup(subklass, phase); } else { diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 1a0872ee0e6..aab2ea3cd3b 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -49,6 +49,9 @@ #include "utilities/ostream.hpp" #include "utilities/powerOfTwo.hpp" #include "utilities/stringUtils.hpp" +#if INCLUDE_SHENANDOAHGC +#include "gc/shenandoah/c2/shenandoahBarrierSetC2.hpp" +#endif // INCLUDE_SHENANDOAHGC // Portions of code courtesy of Clifford Click @@ -732,6 +735,11 @@ void Type::Initialize_shared(Compile* current) { mreg2type[Op_VecY] = TypeVect::VECTY; mreg2type[Op_VecZ] = TypeVect::VECTZ; +#if INCLUDE_SHENANDOAHGC + ShenandoahBarrierSetC2::init(); +#endif //INCLUDE_SHENANDOAHGC + + BarrierSetC2::make_clone_type(); LockNode::initialize_lock_Type(); ArrayCopyNode::initialize_arraycopy_Type(); OptoRuntime::initialize_types(); diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index d332bf440f6..6012bdef86e 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -2020,26 +2020,21 @@ Node* VectorLongToMaskNode::Ideal(PhaseGVN* phase, bool can_reshape) { uint vlen = dst_type->length(); const TypeVectMask* is_mask = dst_type->isa_vectmask(); + // Pattern: (VectorLongToMask (AndL (VectorMaskToLong src) mask)) + // Replace with: (VectorMaskCast src) + // The cast is needed if there are different mask types, and can be folded otherwise. + // The mask has exactly the vlen first bits on: mask = (2 << vlen - 1) if (in(1)->Opcode() == Op_AndL && in(1)->in(1)->Opcode() == Op_VectorMaskToLong && in(1)->in(2)->bottom_type()->isa_long() && in(1)->in(2)->bottom_type()->is_long()->is_con() && - in(1)->in(2)->bottom_type()->is_long()->get_con() == ((1L << vlen) - 1)) { - // Different src/dst mask length represents a re-interpretation operation, - // we can however generate a mask casting operation if length matches. - Node* src = in(1)->in(1)->in(1); - if (is_mask == nullptr) { - if (src->Opcode() != Op_VectorStoreMask) { - return nullptr; - } - src = src->in(1); - } - const TypeVect* src_type = src->bottom_type()->is_vect(); - if (src_type->length() == vlen && - ((src_type->isa_vectmask() == nullptr && is_mask == nullptr) || - (src_type->isa_vectmask() && is_mask))) { - return new VectorMaskCastNode(src, dst_type); - } + in(1)->in(2)->bottom_type()->is_long()->get_con() == ((1LL << vlen) - 1)) { + Node* src = in(1)->in(1)->in(1); + const TypeVect* src_type = src->bottom_type()->is_vect(); + if (src_type->length() == vlen && + ((src_type->isa_vectmask() == nullptr) == (is_mask == nullptr))) { + return new VectorMaskCastNode(src, dst_type); + } } // VectorLongToMask(-1/0) => MaskAll(-1/0) diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index f0d010ee735..dce43f92905 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -1798,13 +1798,23 @@ class VectorStoreMaskNode : public VectorNode { static VectorStoreMaskNode* make(PhaseGVN& gvn, Node* in, BasicType in_type, uint num_elem); }; -// Lane-wise type cast a vector mask to the given vector type. The vector length -// of the input and output must be the same. +// Lane-wise type cast a vector mask to the given vector type. +// The vector length of the input and output must be the same. +// We can only cast between: +// - BVectMask and BVectMask (0x00/0x01) +// - NVectMask and NVectMask (0x0..0/0xF..F of different bit lengths) +// - PVectMask and PVectMask (specialized predicate/mask registers) +// Casting N/PVectMask <-> BVectMask needs to be done by +// VectorStoreMask and VectorLoadMask. class VectorMaskCastNode : public VectorNode { public: VectorMaskCastNode(Node* in, const TypeVect* vt) : VectorNode(in, vt) { const TypeVect* in_vt = in->bottom_type()->is_vect(); assert(in_vt->length() == vt->length(), "vector length must match"); + assert((in_vt->element_basic_type() == T_BOOLEAN) == (vt->element_basic_type() == T_BOOLEAN), + "Cast from/to BVectMask not allowed, use VectorLoadMask/VectorStoreMask instead"); + assert((in_vt->isa_vectmask() == nullptr) == (vt->isa_vectmask() == nullptr), + "Both BVectMask, or both NVectMask, or both PVectMask"); } Node* Identity(PhaseGVN* phase); virtual int Opcode() const; diff --git a/src/hotspot/share/prims/downcallLinker.cpp b/src/hotspot/share/prims/downcallLinker.cpp index 5dde825d75f..cbef9841652 100644 --- a/src/hotspot/share/prims/downcallLinker.cpp +++ b/src/hotspot/share/prims/downcallLinker.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -30,15 +30,34 @@ #include #endif +// keep in synch with jdk.internal.foreign.abi.CapturableState +enum PreservableValues { + NONE = 0, + GET_LAST_ERROR = 1, + WSA_GET_LAST_ERROR = 1 << 1, + ERRNO = 1 << 2 +}; + +// We call this from _thread_in_native, right before a downcall +JVM_LEAF(void, DowncallLinker::capture_state_pre(int32_t* value_ptr, int captured_state_mask)) +#ifdef _WIN64 + if (captured_state_mask & GET_LAST_ERROR) { + SetLastError(*value_ptr); + } + value_ptr++; + if (captured_state_mask & WSA_GET_LAST_ERROR) { + WSASetLastError(*value_ptr); + *value_ptr = WSAGetLastError(); + } + value_ptr++; +#endif + if (captured_state_mask & ERRNO) { + errno = *value_ptr; + } +JVM_END + // We call this from _thread_in_native, right after a downcall -JVM_LEAF(void, DowncallLinker::capture_state(int32_t* value_ptr, int captured_state_mask)) - // keep in synch with jdk.internal.foreign.abi.CapturableState - enum PreservableValues { - NONE = 0, - GET_LAST_ERROR = 1, - WSA_GET_LAST_ERROR = 1 << 1, - ERRNO = 1 << 2 - }; +JVM_LEAF(void, DowncallLinker::capture_state_post(int32_t* value_ptr, int captured_state_mask)) #ifdef _WIN64 if (captured_state_mask & GET_LAST_ERROR) { *value_ptr = GetLastError(); diff --git a/src/hotspot/share/prims/downcallLinker.hpp b/src/hotspot/share/prims/downcallLinker.hpp index 01ee5c56776..2c2cf053033 100644 --- a/src/hotspot/share/prims/downcallLinker.hpp +++ b/src/hotspot/share/prims/downcallLinker.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -41,8 +41,9 @@ public: int captured_state_mask, bool needs_transition); - // This is defined as JVM_LEAF which adds the JNICALL modifier. - static void JNICALL capture_state(int32_t* value_ptr, int captured_state_mask); + // These are defined as JVM_LEAF which adds the JNICALL modifier. + static void JNICALL capture_state_pre(int32_t* value_ptr, int captured_state_mask); + static void JNICALL capture_state_post(int32_t* value_ptr, int captured_state_mask); class StubGenerator : public StubCodeGenerator { BasicType* _signature; diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp index 2297ce9b790..85207fddf29 100644 --- a/src/hotspot/share/prims/jni.cpp +++ b/src/hotspot/share/prims/jni.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2024 Red Hat, Inc. * Copyright (c) 2021, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -3129,16 +3129,21 @@ JNI_END JNI_ENTRY(jobject, jni_GetModule(JNIEnv* env, jclass clazz)) - return Modules::get_module(clazz, THREAD); + HOTSPOT_JNI_GETMODULE_ENTRY(env, clazz); + jobject ret = Modules::get_module(clazz, THREAD); + HOTSPOT_JNI_GETMODULE_RETURN(ret); + return ret; JNI_END JNI_ENTRY(jboolean, jni_IsVirtualThread(JNIEnv* env, jobject obj)) + HOTSPOT_JNI_ISVIRTUALTHREAD_ENTRY(env, obj); + jboolean ret = JNI_FALSE; oop thread_obj = JNIHandles::resolve_external_guard(obj); if (thread_obj != nullptr && thread_obj->is_a(vmClasses::BaseVirtualThread_klass())) { - return JNI_TRUE; - } else { - return JNI_FALSE; + ret = JNI_TRUE; } + HOTSPOT_JNI_ISVIRTUALTHREAD_RETURN(ret); + return ret; JNI_END diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 1a440584fe1..5df1461c0fd 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -578,7 +578,7 @@ WB_END WB_ENTRY(jboolean, WB_G1InConcurrentMark(JNIEnv* env, jobject o)) if (UseG1GC) { G1CollectedHeap* g1h = G1CollectedHeap::heap(); - return g1h->concurrent_mark()->in_progress(); + return g1h->collector_state()->is_in_concurrent_cycle(); } THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_G1InConcurrentMark: G1 GC is not enabled"); WB_END diff --git a/src/hotspot/share/runtime/abstract_vm_version.cpp b/src/hotspot/share/runtime/abstract_vm_version.cpp index 4051ba3f9d6..37c5815f60e 100644 --- a/src/hotspot/share/runtime/abstract_vm_version.cpp +++ b/src/hotspot/share/runtime/abstract_vm_version.cpp @@ -152,28 +152,14 @@ const char* Abstract_VM_Version::vm_info_string() { } case Arguments::_mixed: if (is_vm_statically_linked()) { - if (CompilationModeFlag::quick_only()) { - return CDSConfig::is_using_archive() ? "mixed mode, emulated-client, static, sharing" : "mixed mode, emulated-client, static"; - } else { - return CDSConfig::is_using_archive() ? "mixed mode, static, sharing" : "mixed mode, static"; - } + return CDSConfig::is_using_archive() ? "mixed mode, static, sharing" : "mixed mode, static"; } else { - if (CompilationModeFlag::quick_only()) { - return CDSConfig::is_using_archive() ? "mixed mode, emulated-client, sharing" : "mixed mode, emulated-client"; - } else { - return CDSConfig::is_using_archive() ? "mixed mode, sharing" : "mixed mode"; - } + return CDSConfig::is_using_archive() ? "mixed mode, sharing" : "mixed mode"; } case Arguments::_comp: if (is_vm_statically_linked()) { - if (CompilationModeFlag::quick_only()) { - return CDSConfig::is_using_archive() ? "compiled mode, emulated-client, static, sharing" : "compiled mode, emulated-client, static"; - } return CDSConfig::is_using_archive() ? "compiled mode, static, sharing" : "compiled mode, static"; } else { - if (CompilationModeFlag::quick_only()) { - return CDSConfig::is_using_archive() ? "compiled mode, emulated-client, sharing" : "compiled mode, emulated-client"; - } return CDSConfig::is_using_archive() ? "compiled mode, sharing" : "compiled mode"; } } diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 11c4853c388..dac34017e45 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -537,8 +537,6 @@ static SpecialFlag const special_jvm_flags[] = { { "UseCompressedClassPointers", JDK_Version::jdk(25), JDK_Version::jdk(27), JDK_Version::undefined() }, #endif { "AggressiveHeap", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, - { "NeverActAsServerClassMachine", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, - { "AlwaysActAsServerClassMachine", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, // --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in: { "CreateMinidumpOnCrash", JDK_Version::jdk(9), JDK_Version::undefined(), JDK_Version::undefined() }, @@ -554,6 +552,10 @@ static SpecialFlag const special_jvm_flags[] = { { "ParallelRefProcBalancingEnabled", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, { "MaxRAM", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, { "NewSizeThreadIncrease", JDK_Version::undefined(), JDK_Version::jdk(27), JDK_Version::jdk(28) }, + { "NeverActAsServerClassMachine", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, + { "AlwaysActAsServerClassMachine", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, + { "UseXMMForArrayCopy", JDK_Version::undefined(), JDK_Version::jdk(27), JDK_Version::jdk(28) }, + { "UseNewLongLShift", JDK_Version::undefined(), JDK_Version::jdk(27), JDK_Version::jdk(28) }, #ifdef ASSERT { "DummyObsoleteTestFlag", JDK_Version::undefined(), JDK_Version::jdk(18), JDK_Version::undefined() }, @@ -1513,10 +1515,7 @@ void Arguments::set_heap_size() { !FLAG_IS_DEFAULT(MinRAMPercentage) || !FLAG_IS_DEFAULT(InitialRAMPercentage); - // Limit the available memory if client emulation mode is enabled. - const size_t avail_mem = CompilerConfig::should_set_client_emulation_mode_flags() - ? 1ULL*G - : os::physical_memory(); + const size_t avail_mem = os::physical_memory(); // If the maximum heap size has not been set with -Xmx, then set it as // fraction of the size of physical memory, respecting the maximum and diff --git a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp index 444ce321759..36eece6f013 100644 --- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp +++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp @@ -274,6 +274,17 @@ JVMFlag::Error AVX3ThresholdConstraintFunc(int value, bool verbose) { return JVMFlag::SUCCESS; } +JVMFlag::Error CopyAVX3ThresholdConstraintFunc(int value, bool verbose) { + if (value != 0 && !is_power_of_2(value)) { + JVMFlag::printError(verbose, + "CopyAVX3Threshold ( %d ) must be 0 or " + "a power of two value between 0 and MAX_INT\n", value); + return JVMFlag::VIOLATES_CONSTRAINT; + } + + return JVMFlag::SUCCESS; +} + JVMFlag::Error ArraycopySrcPrefetchDistanceConstraintFunc(uintx value, bool verbose) { if (value >= 4032) { JVMFlag::printError(verbose, diff --git a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.hpp b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.hpp index cf785800cfc..45e91058e0b 100644 --- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.hpp +++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.hpp @@ -46,6 +46,7 @@ f(uintx, ArraycopyDstPrefetchDistanceConstraintFunc) \ f(uintx, ArraycopySrcPrefetchDistanceConstraintFunc) \ f(int, AVX3ThresholdConstraintFunc) \ + f(int, CopyAVX3ThresholdConstraintFunc) \ f(uint, TypeProfileLevelConstraintFunc) \ f(uint, VerifyIterativeGVNConstraintFunc) \ f(intx, InitArrayShortSizeConstraintFunc) \ diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 6d4b9908e1c..17e10e2f87c 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -199,19 +199,6 @@ const int ObjectAlignmentInBytes = 8; "Granularity to use for NUMA interleaving on Windows OS") \ constraint(NUMAInterleaveGranularityConstraintFunc, AtParse) \ \ - product(uintx, NUMAChunkResizeWeight, 20, \ - "Percentage (0-100) used to weight the current sample when " \ - "computing exponentially decaying average for " \ - "AdaptiveNUMAChunkSizing") \ - range(0, 100) \ - \ - product(size_t, NUMASpaceResizeRate, 1*G, \ - "Do not reallocate more than this amount per collection") \ - range(0, max_uintx) \ - \ - product(bool, UseAdaptiveNUMAChunkSizing, true, \ - "Enable adaptive chunk sizing for NUMA") \ - \ product(bool, NUMAStats, false, \ "Print NUMA stats in detailed heap information") \ \ @@ -811,9 +798,6 @@ const int ObjectAlignmentInBytes = 8; "Number of OutOfMemoryErrors preallocated with backtrace") \ range(0, 1024) \ \ - product(bool, UseXMMForArrayCopy, false, \ - "Use SSE2 MOVQ instruction for Arraycopy") \ - \ develop(bool, PrintFieldLayout, false, \ "Print field layout for each class") \ \ @@ -884,21 +868,9 @@ const int ObjectAlignmentInBytes = 8; develop(bool, VerifyDependencies, trueInDebug, \ "Exercise and verify the compilation dependency mechanism") \ \ - develop(bool, TraceNewOopMapGeneration, false, \ - "Trace OopMapGeneration") \ - \ - develop(bool, TraceNewOopMapGenerationDetailed, false, \ - "Trace OopMapGeneration: print detailed cell states") \ - \ develop(bool, TimeOopMap, false, \ "Time calls to GenerateOopMap::compute_map() in sum") \ \ - develop(bool, TimeOopMap2, false, \ - "Time calls to GenerateOopMap::compute_map() individually") \ - \ - develop(bool, TraceOopMapRewrites, false, \ - "Trace rewriting of methods during oop map generation") \ - \ develop(bool, TraceFinalizerRegistration, false, \ "Trace registration of final references") \ \ diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index cb69b82d767..94399088464 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -593,12 +593,15 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonito OSThreadContendState osts(current->osthread()); assert(current->thread_state() == _thread_in_vm, "invariant"); + ObjectWaiter node(current); for (;;) { ExitOnSuspend eos(this); { ThreadBlockInVMPreprocess tbivs(current, eos, true /* allow_suspend */); - enter_internal(current); + if (!try_enter_fast(current, &node)) { + enter_internal(current, &node, false /* reenter_path */); + } current->set_current_pending_monitor(nullptr); // We can go to a safepoint at the end of this block. If we // do a thread dump during that safepoint, then this thread will show @@ -942,14 +945,17 @@ const char* ObjectMonitor::is_busy_to_string(stringStream* ss) { return ss->base(); } -void ObjectMonitor::enter_internal(JavaThread* current) { +bool ObjectMonitor::try_enter_fast(JavaThread* current, ObjectWaiter* current_node) { + assert(current != nullptr, "invariant"); assert(current->thread_state() == _thread_blocked, "invariant"); + assert(current_node != nullptr, "invariant"); + assert(current_node->_thread == current, "invariant"); // Try the lock - TATAS if (try_lock(current) == TryLockResult::Success) { assert(!has_successor(current), "invariant"); assert(has_owner(current), "invariant"); - return; + return true; } assert(InitDone, "Unexpectedly not initialized"); @@ -964,7 +970,7 @@ void ObjectMonitor::enter_internal(JavaThread* current) { if (try_spin(current)) { assert(has_owner(current), "invariant"); assert(!has_successor(current), "invariant"); - return; + return true; } // The Spin failed -- Enqueue and park the thread ... @@ -973,54 +979,67 @@ void ObjectMonitor::enter_internal(JavaThread* current) { // Enqueue "current" on ObjectMonitor's _entry_list. // - // Node acts as a proxy for current. + // current_node acts as a proxy for current. // As an aside, if were to ever rewrite the synchronization code mostly // in Java, WaitNodes, ObjectMonitors, and Events would become 1st-class // Java objects. This would avoid awkward lifecycle and liveness issues, // as well as eliminate a subset of ABA issues. // TODO: eliminate ObjectWaiter and enqueue either Threads or Events. - ObjectWaiter node(current); current->_ParkEvent->reset(); - if (try_lock_or_add_to_entry_list(current, &node)) { - return; // We got the lock. + if (try_lock_or_add_to_entry_list(current, current_node)) { + return true; // We got the lock. } + // This thread is now added to the _entry_list. // The lock might have been released while this thread was occupied queueing // itself onto _entry_list. To close the race and avoid "stranding" and - // progress-liveness failure we must resample-retry _owner before parking. + // progress-liveness failure the caller must resample-retry _owner before parking. // Note the Dekker/Lamport duality: ST _entry_list; MEMBAR; LD Owner. - // In this case the ST-MEMBAR is accomplished with CAS(). - // - // TODO: Defer all thread state transitions until park-time. - // Since state transitions are heavy and inefficient we'd like - // to defer the state transitions until absolutely necessary, - // and in doing so avoid some transitions ... + // In this case the ST-MEMBAR is accomplished with CAS() in try_lock_or_add_to_entry_list. + return false; +} + +void ObjectMonitor::enter_internal(JavaThread* current, ObjectWaiter* current_node, bool reenter_path) { + assert(current != nullptr, "invariant"); + assert(current->thread_state() == _thread_blocked, "invariant"); + assert(current_node != nullptr, "invariant"); + assert(current_node->_thread == current, "invariant"); // If there are unmounted virtual threads ahead in the _entry_list we want // to do a timed-park instead to alleviate some deadlock cases where one // of them is picked as the successor but cannot run due to having run out // of carriers. This can happen, for example, if this is a pinned virtual - // thread currently loading or initializining a class, and all other carriers + // thread currently loading or initializing a class, and all other carriers // have a pinned vthread waiting for said class to be loaded/initialized. // Read counter *after* adding this thread to the _entry_list. Adding to // _entry_list uses Atomic::cmpxchg() which already provides a fence that - // prevents this load from floating up previous store. + // prevents this load from floating up past a previous store. // Note that we can have false positives where timed-park is not necessary. - bool do_timed_parked = has_unmounted_vthreads(); + bool do_timed_park = has_unmounted_vthreads(); jlong recheck_interval = 1; for (;;) { + ObjectWaiter::TStates v = current_node->TState; + guarantee(v == ObjectWaiter::TS_ENTER, "invariant"); if (try_lock(current) == TryLockResult::Success) { break; } assert(!has_owner(current), "invariant"); + if (reenter_path) { + // If try_lock failed, spin again - we expect the notifier to release the monitor quickly. + // Note that spin count may be zero so the above try_lock is necessary. + if (try_spin(current)) { + break; + } + } + // park self - if (do_timed_parked) { + if (do_timed_park) { current->_ParkEvent->park(recheck_interval); // Increase the recheck_interval, but clamp the value. recheck_interval *= 8; @@ -1031,125 +1050,6 @@ void ObjectMonitor::enter_internal(JavaThread* current) { current->_ParkEvent->park(); } - if (try_lock(current) == TryLockResult::Success) { - break; - } - - // The lock is still contested. - - // Assuming this is not a spurious wakeup we'll normally find _succ == current. - // We can defer clearing _succ until after the spin completes - // try_spin() must tolerate being called with _succ == current. - // Try yet another round of adaptive spinning. - if (try_spin(current)) { - break; - } - - // We can find that we were unpark()ed and redesignated _succ while - // we were spinning. That's harmless. If we iterate and call park(), - // park() will consume the event and return immediately and we'll - // just spin again. This pattern can repeat, leaving _succ to simply - // spin on a CPU. - - if (has_successor(current)) clear_successor(); - - // Invariant: after clearing _succ a thread *must* retry _owner before parking. - OrderAccess::fence(); - } - - // Egress : - // Current has acquired the lock -- Unlink current from the _entry_list. - unlink_after_acquire(current, &node); - if (has_successor(current)) { - clear_successor(); - // Note that we don't need to do OrderAccess::fence() after clearing - // _succ here, since we own the lock. - } - - // We've acquired ownership with CAS(). - // CAS is serializing -- it has MEMBAR/FENCE-equivalent semantics. - // But since the CAS() this thread may have also stored into _succ - // or entry_list. These meta-data updates must be visible __before - // this thread subsequently drops the lock. - // Consider what could occur if we didn't enforce this constraint -- - // STs to monitor meta-data and user-data could reorder with (become - // visible after) the ST in exit that drops ownership of the lock. - // Some other thread could then acquire the lock, but observe inconsistent - // or old monitor meta-data and heap data. That violates the JMM. - // To that end, the exit() operation must have at least STST|LDST - // "release" barrier semantics. Specifically, there must be at least a - // STST|LDST barrier in exit() before the ST of null into _owner that drops - // the lock. The barrier ensures that changes to monitor meta-data and data - // protected by the lock will be visible before we release the lock, and - // therefore before some other thread (CPU) has a chance to acquire the lock. - // See also: http://gee.cs.oswego.edu/dl/jmm/cookbook.html. - // - // Critically, any prior STs to _succ or entry_list must be visible before - // the ST of null into _owner in the *subsequent* (following) corresponding - // monitorexit. - - return; -} - -// reenter_internal() is a specialized inline form of the latter half of the -// contended slow-path from enter_internal(). We use reenter_internal() only for -// monitor reentry in wait(). -// -// In the future we should reconcile enter_internal() and reenter_internal(). - -void ObjectMonitor::reenter_internal(JavaThread* current, ObjectWaiter* currentNode) { - assert(current != nullptr, "invariant"); - assert(current->thread_state() == _thread_blocked, "invariant"); - assert(currentNode != nullptr, "invariant"); - assert(currentNode->_thread == current, "invariant"); - assert(_waiters > 0, "invariant"); - - // If there are unmounted virtual threads ahead in the _entry_list we want - // to do a timed-park instead to alleviate some deadlock cases where one - // of them is picked as the successor but cannot run due to having run out - // of carriers. This can happen, for example, if a mixed of unmounted and - // pinned vthreads taking up all the carriers are waiting for a class to be - // initialized, and the selected successor is one of the unmounted vthreads. - // Although this method is used for the "notification" case, it could be - // that this thread reached here without been added to the _entry_list yet. - // This can happen if it was interrupted or the wait timed-out at the same - // time. In that case we rely on currentNode->_do_timed_park, which will be - // read on the next loop iteration, after consuming the park permit set by - // the notifier in notify_internal. - // Note that we can have false positives where timed-park is not necessary. - bool do_timed_parked = has_unmounted_vthreads(); - jlong recheck_interval = 1; - - for (;;) { - ObjectWaiter::TStates v = currentNode->TState; - guarantee(v == ObjectWaiter::TS_ENTER, "invariant"); - assert(!has_owner(current), "invariant"); - - // This thread has been notified so try to reacquire the lock. - if (try_lock(current) == TryLockResult::Success) { - break; - } - - // If that fails, spin again. Note that spin count may be zero so the above TryLock - // is necessary. - if (try_spin(current)) { - break; - } - - { - OSThreadContendState osts(current->osthread()); - if (do_timed_parked) { - current->_ParkEvent->park(recheck_interval); - // Increase the recheck_interval, but clamp the value. - recheck_interval *= 8; - if (recheck_interval > MAX_RECHECK_INTERVAL) { - recheck_interval = MAX_RECHECK_INTERVAL; - } - } else { - current->_ParkEvent->park(); - } - } - // Try again, but just so we distinguish between futile wakeups and // successful wakeups. The following test isn't algorithmically // necessary, but it helps us maintain sensible statistics. @@ -1159,25 +1059,68 @@ void ObjectMonitor::reenter_internal(JavaThread* current, ObjectWaiter* currentN // The lock is still contested. - // Assuming this is not a spurious wakeup we'll normally - // find that _succ == current. - if (has_successor(current)) clear_successor(); + if (!reenter_path) { + // Assuming this is not a spurious wakeup we'll normally find _succ == current. + // We can defer clearing _succ until after the spin completes and + // try_spin() must tolerate being called with _succ == current. + // Try yet another round of adaptive spinning. + if (try_spin(current)) { + break; + } + } - // Invariant: after clearing _succ a contending thread - // *must* retry _owner before parking. + // We can find that we were unpark()ed and redesignated _succ while + // we were spinning. That's harmless. If we iterate and call park(), + // park() will consume the event and return immediately and we'll + // just spin again. This pattern can repeat, leaving _succ to simply + // spin on a CPU. + + if (has_successor(current)) { + clear_successor(); + } + + // Invariant: after clearing _succ a thread *must* retry _owner before parking. OrderAccess::fence(); - // See comment in notify_internal - do_timed_parked |= currentNode->_do_timed_park; + // Will only potentially change on the reenter path - see comment in notify_internal. + do_timed_park |= current_node->_do_timed_park; } - // Current has acquired the lock -- Unlink current from the _entry_list. assert(has_owner(current), "invariant"); - unlink_after_acquire(current, currentNode); - if (has_successor(current)) clear_successor(); - assert(!has_successor(current), "invariant"); - currentNode->TState = ObjectWaiter::TS_RUN; - OrderAccess::fence(); // see comments at the end of enter_internal() + + // Egress : + // Current has acquired the lock -- Unlink current from the _entry_list. + unlink_after_acquire(current, current_node); + if (has_successor(current)) { + clear_successor(); + // Note that we don't need to do OrderAccess::fence() after clearing + // _succ here, since we own the lock. + } + + // We've acquired ownership with CAS(). + // CAS is serializing -- it has MEMBAR/FENCE-equivalent semantics. + // But since the CAS() this thread may have also stored into _succ + // or entry_list. These meta-data updates must be visible __before + // this thread subsequently drops the lock. + // Consider what could occur if we didn't enforce this constraint -- + // STs to monitor meta-data and user-data could reorder with (become + // visible after) the ST in exit that drops ownership of the lock. + // Some other thread could then acquire the lock, but observe inconsistent + // or old monitor meta-data and heap data. That violates the JMM. + // To that end, the exit() operation must have at least STST|LDST + // "release" barrier semantics. Specifically, there must be at least a + // STST|LDST barrier in exit() before the ST of null into _owner that drops + // the lock. The barrier ensures that changes to monitor meta-data and data + // protected by the lock will be visible before we release the lock, and + // therefore before some other thread (CPU) has a chance to acquire the lock. + // See also: http://gee.cs.oswego.edu/dl/jmm/cookbook.html. + // + // Critically, any prior STs to _succ or entry_list must be visible before + // the ST of null into _owner in the *subsequent* (following) corresponding + // monitorexit. + + current_node->TState = ObjectWaiter::TS_RUN; + return; } // This method is called from two places: @@ -1971,7 +1914,9 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { ExitOnSuspend eos(this); { ThreadBlockInVMPreprocess tbivs(current, eos, true /* allow_suspend */); - reenter_internal(current, &node); + assert( _waiters > 0, "invariant"); + OSThreadContendState osts(current->osthread()); + enter_internal(current, &node, true /* reenter_path */); // We can go to a safepoint at the end of this block. If we // do a thread dump during that safepoint, then this thread will show // as having "-locked" the monitor, but the OS and java.lang.Thread @@ -2082,12 +2027,12 @@ bool ObjectMonitor::notify_internal(JavaThread* current) { // Wake up the thread to alleviate some deadlock cases where the successor // that will be picked up when this thread releases the monitor is an unmounted // virtual thread that cannot run due to having run out of carriers. Upon waking - // up, the thread will call reenter_internal() which will use timed-park in case + // up, the thread will call enter_internal(..., true) which will use timed-park in case // there is contention and there are still vthreads in the _entry_list. // If the target was interrupted or the wait timed-out at the same time, it could - // have reached reenter_internal and read a false value of has_unmounted_vthreads() + // have reached enter_internal and read a false value of has_unmounted_vthreads() // before we added it to the _entry_list above. To deal with that case, we set _do_timed_park - // which will be read by the target on the next loop iteration in reenter_internal. + // which will be read by the target on the next loop iteration in enter_internal. iterator->_do_timed_park = true; JavaThread* t = iterator->thread(); t->_ParkEvent->unpark(); diff --git a/src/hotspot/share/runtime/objectMonitor.hpp b/src/hotspot/share/runtime/objectMonitor.hpp index 8d9a481bf37..842aa1b374e 100644 --- a/src/hotspot/share/runtime/objectMonitor.hpp +++ b/src/hotspot/share/runtime/objectMonitor.hpp @@ -394,8 +394,8 @@ class ObjectMonitor : public CHeapObj { bool notify_internal(JavaThread* current); ObjectWaiter* dequeue_waiter(); void dequeue_specific_waiter(ObjectWaiter* waiter); - void enter_internal(JavaThread* current); - void reenter_internal(JavaThread* current, ObjectWaiter* current_node); + void enter_internal(JavaThread* current, ObjectWaiter* current_node, bool reenter_path); + bool try_enter_fast(JavaThread* current, ObjectWaiter* current_node); void entry_list_build_dll(JavaThread* current); void unlink_after_acquire(JavaThread* current, ObjectWaiter* current_node); ObjectWaiter* entry_list_tail(JavaThread* current); diff --git a/src/hotspot/share/runtime/objectMonitorTable.cpp b/src/hotspot/share/runtime/objectMonitorTable.cpp index 8431b6dd0d4..bc173992d7a 100644 --- a/src/hotspot/share/runtime/objectMonitorTable.cpp +++ b/src/hotspot/share/runtime/objectMonitorTable.cpp @@ -31,6 +31,7 @@ #include "runtime/thread.hpp" #include "runtime/timerTrace.hpp" #include "runtime/trimNativeHeap.hpp" +#include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" // ----------------------------------------------------------------------------- @@ -68,7 +69,7 @@ // allocate a new table (twice as large as the old one), and then copy all the // old monitor pointers from the old table to the new. // -// But since the OMT is a concurrent hash table and things needs to work for +// But since the OMT is a concurrent hash table and things need to work for // other clients of the OMT while we grow it, it gets a bit more // complicated. // @@ -117,17 +118,17 @@ // requirements. Don't change it for fun, it might backfire. // ----------------------------------------------------------------------------- -ObjectMonitorTable::Table* volatile ObjectMonitorTable::_curr; +Atomic ObjectMonitorTable::_curr; class ObjectMonitorTable::Table : public CHeapObj { friend class ObjectMonitorTable; DEFINE_PAD_MINUS_SIZE(0, DEFAULT_CACHE_LINE_SIZE, 0); const size_t _capacity_mask; // One less than its power-of-two capacity - Table* volatile _prev; // Set while rehashing - Entry volatile* _buckets; // The payload + Atomic _prev; // Set while growing/rebuilding + Atomic* _buckets; // The payload DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, sizeof(_capacity_mask) + sizeof(_prev) + sizeof(_buckets)); - volatile size_t _items_count; + Atomic _items_count; DEFINE_PAD_MINUS_SIZE(2, DEFAULT_CACHE_LINE_SIZE, sizeof(_items_count)); static Entry as_entry(ObjectMonitor* monitor) { @@ -156,11 +157,11 @@ class ObjectMonitorTable::Table : public CHeapObj { // Make sure we leave space for previous versions to relocate too. bool try_inc_items_count() { for (;;) { - size_t population = AtomicAccess::load(&_items_count); + size_t population = _items_count.load_relaxed(); if (should_grow(population)) { return false; } - if (AtomicAccess::cmpxchg(&_items_count, population, population + 1, memory_order_relaxed) == population) { + if (_items_count.compare_set(population, population + 1, memory_order_relaxed)) { return true; } } @@ -171,31 +172,31 @@ class ObjectMonitorTable::Table : public CHeapObj { } void inc_items_count() { - AtomicAccess::inc(&_items_count, memory_order_relaxed); + _items_count.add_then_fetch(1u, memory_order_relaxed); } void dec_items_count() { - AtomicAccess::dec(&_items_count, memory_order_relaxed); + _items_count.sub_then_fetch(1u, memory_order_relaxed); } public: Table(size_t capacity, Table* prev) : _capacity_mask(capacity - 1), _prev(prev), - _buckets(NEW_C_HEAP_ARRAY(Entry, capacity, mtObjectMonitor)), + _buckets(NEW_C_HEAP_ARRAY(Atomic, capacity, mtObjectMonitor)), _items_count(0) { for (size_t i = 0; i < capacity; ++i) { - _buckets[i] = empty(); + ::new (_buckets + i) Atomic(empty()); } } ~Table() { - FREE_C_HEAP_ARRAY(Entry, _buckets); + FREE_C_HEAP_ARRAY(Atomic, _buckets); } Table* prev() { - return AtomicAccess::load(&_prev); + return _prev.load_relaxed(); } size_t capacity() { @@ -207,12 +208,12 @@ public: } bool should_grow() { - return should_grow(AtomicAccess::load(&_items_count)); + return should_grow(_items_count.load_relaxed()); } size_t total_items() { - size_t current_items = AtomicAccess::load(&_items_count); - Table* prev = AtomicAccess::load(&_prev); + size_t current_items = _items_count.load_relaxed(); + Table* prev = _prev.load_relaxed(); if (prev != nullptr) { return prev->total_items() + current_items; } @@ -221,7 +222,7 @@ public: ObjectMonitor* get(oop obj, intptr_t hash) { // Acquire tombstones and relocations in case prev transitioned to null - Table* prev = AtomicAccess::load_acquire(&_prev); + Table* prev = _prev.load_acquire(); if (prev != nullptr) { ObjectMonitor* result = prev->get(obj, hash); if (result != nullptr) { @@ -233,8 +234,8 @@ public: size_t index = start_index; for (;;) { - Entry volatile* bucket = _buckets + index; - Entry entry = AtomicAccess::load_acquire(bucket); + Atomic& bucket = _buckets[index]; + Entry entry = bucket.load_acquire(); if (entry == tombstone() || entry == empty()) { // Not found @@ -250,14 +251,14 @@ public: assert(index != start_index, "invariant"); } - // Rehashing could have started by now, but if a monitor has been inserted in a - // newer table, it was inserted after the get linearization point. + // Rebuilding could have started by now, but if a monitor has been inserted + // in a newer table, it was inserted after the get linearization point. return nullptr; } ObjectMonitor* prepare_insert(oop obj, intptr_t hash) { // Acquire any tombstones and relocations if prev transitioned to null. - Table* prev = AtomicAccess::load_acquire(&_prev); + Table* prev = _prev.load_acquire(); if (prev != nullptr) { ObjectMonitor* result = prev->prepare_insert(obj, hash); if (result != nullptr) { @@ -269,15 +270,15 @@ public: size_t index = start_index; for (;;) { - Entry volatile* bucket = _buckets + index; - Entry entry = AtomicAccess::load_acquire(bucket); + Atomic& bucket = _buckets[index]; + Entry entry = bucket.load_acquire(); if (entry == empty()) { // Found an empty slot to install the new monitor in. // To avoid concurrent inserts succeeding, place a tombstone here. - Entry result = AtomicAccess::cmpxchg(bucket, entry, tombstone(), memory_order_relaxed); + Entry result = bucket.compare_exchange(entry, tombstone(), memory_order_relaxed); if (result == entry) { - // Success! Nobody will try to insert here again, except reinsert from rehashing. + // Success! Nobody will try to insert here again, except reinsert from rebuilding. return nullptr; } entry = result; @@ -300,7 +301,7 @@ public: ObjectMonitor* get_set(oop obj, Entry new_monitor, intptr_t hash) { // Acquire any tombstones and relocations if prev transitioned to null. - Table* prev = AtomicAccess::load_acquire(&_prev); + Table* prev = _prev.load_acquire(); if (prev != nullptr) { // Sprinkle tombstones in previous tables to force concurrent inserters // to the latest table. We only really want to try inserting in the @@ -315,14 +316,14 @@ public: size_t index = start_index; for (;;) { - Entry volatile* bucket = _buckets + index; - Entry entry = AtomicAccess::load_acquire(bucket); + Atomic& bucket = _buckets[index]; + Entry entry = bucket.load_acquire(); if (entry == empty()) { // Empty slot to install the new monitor if (try_inc_items_count()) { // Succeeding in claiming an item. - Entry result = AtomicAccess::cmpxchg(bucket, entry, new_monitor, memory_order_acq_rel); + Entry result = bucket.compare_exchange(entry, new_monitor, memory_order_acq_rel); if (result == entry) { // Success - already incremented. return as_monitor(new_monitor); @@ -332,11 +333,11 @@ public: dec_items_count(); entry = result; } else { - // Out of allowance; leaving place for rehashing to succeed. + // Out of allowance; leave space for rebuilding to succeed. // To avoid concurrent inserts succeeding, place a tombstone here. - Entry result = AtomicAccess::cmpxchg(bucket, entry, tombstone(), memory_order_acq_rel); + Entry result = bucket.compare_exchange(entry, tombstone(), memory_order_acq_rel); if (result == entry) { - // Success; nobody will try to insert here again, except reinsert from rehashing. + // Success; nobody will try to insert here again, except reinsert from rebuilding. return nullptr; } entry = result; @@ -366,8 +367,8 @@ public: size_t index = start_index; for (;;) { - Entry volatile* bucket = _buckets + index; - Entry entry = AtomicAccess::load_acquire(bucket); + Atomic& bucket = _buckets[index]; + Entry entry = bucket.load_acquire(); if (entry == empty()) { // The monitor does not exist in this table. @@ -381,8 +382,8 @@ public: if (entry == old_monitor) { // Found matching entry; remove it - Entry result = AtomicAccess::cmpxchg(bucket, entry, removed(), memory_order_relaxed); - assert(result == entry, "should not fail"); + bool result = bucket.compare_set(entry, removed(), memory_order_relaxed); + assert(result, "should not fail"); break; } @@ -395,8 +396,8 @@ public: // still not being a monitor, instead of flickering back to being there. // Only the deflation thread rebuilds and unlinks tables, so we do not need // any concurrency safe prev read below. - if (_prev != nullptr) { - _prev->remove(obj, old_monitor, hash); + if (_prev.load_relaxed() != nullptr) { + _prev.load_relaxed()->remove(obj, old_monitor, hash); } } @@ -407,12 +408,12 @@ public: size_t index = start_index; for (;;) { - Entry volatile* bucket = _buckets + index; - Entry entry = AtomicAccess::load_acquire(bucket); + Atomic& bucket = _buckets[index]; + Entry entry = bucket.load_acquire(); if (entry == empty()) { // Empty slot to install the new monitor. - Entry result = AtomicAccess::cmpxchg(bucket, entry, new_monitor, memory_order_acq_rel); + Entry result = bucket.compare_exchange(entry, new_monitor, memory_order_acq_rel); if (result == entry) { // Success - unconditionally increment. inc_items_count(); @@ -426,7 +427,7 @@ public: if (entry == tombstone()) { // A concurrent inserter did not get enough allowance in the table. // But reinsert always succeeds - we will take the spot. - Entry result = AtomicAccess::cmpxchg(bucket, entry, new_monitor, memory_order_acq_rel); + Entry result = bucket.compare_exchange(entry, new_monitor, memory_order_acq_rel); if (result == entry) { // Success - unconditionally increment. inc_items_count(); @@ -439,7 +440,7 @@ public: if (entry == removed()) { // A removed entry can be flipped back with reinsertion. - Entry result = AtomicAccess::cmpxchg(bucket, entry, new_monitor, memory_order_release); + Entry result = bucket.compare_exchange(entry, new_monitor, memory_order_release); if (result == entry) { // Success - but don't increment; the initial entry did that for us. return; @@ -459,7 +460,7 @@ public: } void rebuild() { - Table* prev = _prev; + Table* prev = _prev.load_relaxed(); if (prev == nullptr) { // Base case for recursion - no previous version. return; @@ -477,12 +478,12 @@ public: ThreadBlockInVM tbivm(current); } - Entry volatile* bucket = prev->_buckets + index; - Entry entry = AtomicAccess::load_acquire(bucket); + Atomic& bucket = prev->_buckets[index]; + Entry entry = bucket.load_acquire(); if (entry == empty()) { // Empty slot; put a tombstone there. - Entry result = AtomicAccess::cmpxchg(bucket, entry, tombstone(), memory_order_acq_rel); + Entry result = bucket.compare_exchange(entry, tombstone(), memory_order_acq_rel); if (result == empty()) { // Success; move to next entry. continue; @@ -508,17 +509,17 @@ public: } // Unlink this table, releasing the tombstones and relocations. - AtomicAccess::release_store(&_prev, (Table*)nullptr); + _prev.release_store(nullptr); } }; void ObjectMonitorTable::create() { - _curr = new Table(128, nullptr); + _curr.store_relaxed(new Table(128, nullptr)); } ObjectMonitor* ObjectMonitorTable::monitor_get(oop obj) { const intptr_t hash = obj->mark().hash(); - Table* curr = AtomicAccess::load_acquire(&_curr); + Table* curr = _curr.load_acquire(); ObjectMonitor* monitor = curr->get(obj, hash); return monitor; } @@ -526,7 +527,7 @@ ObjectMonitor* ObjectMonitorTable::monitor_get(oop obj) { // Returns a new table to try inserting into. ObjectMonitorTable::Table* ObjectMonitorTable::grow_table(Table* curr) { Table* result; - Table* new_table = AtomicAccess::load_acquire(&_curr); + Table* new_table = _curr.load_acquire(); if (new_table != curr) { // Table changed; no need to try further return new_table; @@ -537,14 +538,14 @@ ObjectMonitorTable::Table* ObjectMonitorTable::grow_table(Table* curr) { // attempt to allocate the new table. MonitorLocker ml(MonitorDeflation_lock, Mutex::_no_safepoint_check_flag); - new_table = AtomicAccess::load_acquire(&_curr); + new_table = _curr.load_acquire(); if (new_table != curr) { // Table changed; no need to try further return new_table; } new_table = new Table(curr->capacity() << 1, curr); - result = AtomicAccess::cmpxchg(&_curr, curr, new_table, memory_order_acq_rel); + result = _curr.compare_exchange(curr, new_table, memory_order_acq_rel); if (result == curr) { log_info(monitorinflation)("Growing object monitor table (capacity: %zu)", new_table->capacity()); @@ -557,7 +558,7 @@ ObjectMonitorTable::Table* ObjectMonitorTable::grow_table(Table* curr) { } } - // Somebody else started rehashing; restart in new table. + // Somebody else started rebuilding; restart in their new table. delete new_table; return result; @@ -565,7 +566,7 @@ ObjectMonitorTable::Table* ObjectMonitorTable::grow_table(Table* curr) { ObjectMonitor* ObjectMonitorTable::monitor_put_get(ObjectMonitor* monitor, oop obj) { const intptr_t hash = obj->mark().hash(); - Table* curr = AtomicAccess::load_acquire(&_curr); + Table* curr = _curr.load_acquire(); for (;;) { // Curr is the latest table and is reasonably loaded. @@ -585,12 +586,12 @@ void ObjectMonitorTable::remove_monitor_entry(ObjectMonitor* monitor) { return; } const intptr_t hash = obj->mark().hash(); - Table* curr = AtomicAccess::load_acquire(&_curr); + Table* curr = _curr.load_acquire(); curr->remove(obj, curr->as_entry(monitor), hash); assert(monitor_get(obj) != monitor, "should have been removed"); } -// Before handshake; rehash and unlink tables. +// Before handshake; rebuild and unlink tables. void ObjectMonitorTable::rebuild(GrowableArray* delete_list) { Table* new_table; { @@ -620,15 +621,15 @@ void ObjectMonitorTable::rebuild(GrowableArray* delete_list) { // given the growing threshold of 12.5%, it is impossible for the // tables to reach a load factor above 50%. Which is more than // enough to guarantee the function of this concurrent hash table. - Table* curr = AtomicAccess::load_acquire(&_curr); + Table* curr = _curr.load_acquire(); size_t need_to_accomodate = curr->total_items(); size_t new_capacity = curr->should_grow(need_to_accomodate) ? curr->capacity() << 1 : curr->capacity(); new_table = new Table(new_capacity, curr); - Table* result = AtomicAccess::cmpxchg(&_curr, curr, new_table, memory_order_acq_rel); + Table* result = _curr.compare_exchange(curr, new_table, memory_order_acq_rel); if (result != curr) { - // Somebody else racingly started rehashing. Delete the + // Somebody else racingly started rebuilding. Delete the // new_table and treat somebody else's table as the new one. delete new_table; new_table = result; @@ -653,7 +654,7 @@ void ObjectMonitorTable::destroy(GrowableArray* delete_list) { } address ObjectMonitorTable::current_table_address() { - return (address)(&_curr); + return reinterpret_cast
(&_curr) + _curr.value_offset_in_bytes(); } ByteSize ObjectMonitorTable::table_capacity_mask_offset() { @@ -661,5 +662,9 @@ ByteSize ObjectMonitorTable::table_capacity_mask_offset() { } ByteSize ObjectMonitorTable::table_buckets_offset() { + // Assumptions made from the emitted code about the layout. + STATIC_ASSERT(sizeof(Atomic) == sizeof(Entry*)); + STATIC_ASSERT(Atomic::value_offset_in_bytes() == 0); + return byte_offset_of(Table, _buckets); } diff --git a/src/hotspot/share/runtime/objectMonitorTable.hpp b/src/hotspot/share/runtime/objectMonitorTable.hpp index 0a56b696069..1ec372883b2 100644 --- a/src/hotspot/share/runtime/objectMonitorTable.hpp +++ b/src/hotspot/share/runtime/objectMonitorTable.hpp @@ -27,6 +27,7 @@ #include "memory/allStatic.hpp" #include "oops/oopsHierarchy.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/sizes.hpp" @@ -44,7 +45,7 @@ public: class Table; private: - static Table* volatile _curr; + static Atomic _curr; static Table* grow_table(Table* curr); enum class Entry : uintptr_t { diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 16335f97fdb..129f8f76e73 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -1912,17 +1912,7 @@ void os::trace_page_sizes_for_requested_size(const char* str, // as was done for logical processors here, or replicate and // specialize this method for each platform. (Or fix os to have // some inheritance structure and use subclassing. Sigh.) -// If you want some platform to always or never behave as a server -// class machine, change the setting of AlwaysActAsServerClassMachine -// and NeverActAsServerClassMachine in globals*.hpp. bool os::is_server_class_machine() { - // First check for the early returns - if (NeverActAsServerClassMachine) { - return false; - } - if (AlwaysActAsServerClassMachine) { - return true; - } // Then actually look at the machine bool result = false; const unsigned int server_processors = 2; @@ -2312,24 +2302,13 @@ void os::uncommit_memory(char* addr, size_t bytes, bool executable) { log_debug(os, map)("Uncommitted " RANGEFMT, RANGEFMTARGS(addr, bytes)); } -// The scope of NmtVirtualMemoryLocker covers both pd_release_memory and record_virtual_memory_release because -// these operations must happen atomically to avoid races causing NMT to fall out os sync with the OS reality. -// We do not have the same lock protection for pd_reserve_memory and record_virtual_memory_reserve. -// We assume that there is some external synchronization that prevents a region from being released -// before it is finished being reserved. +// pd_release_memory is called outside the protection of the NMT lock. +// Until pd_release_memory is called, The OS is unable to give away the about-to-be-released range to another thread. +// So there is no risk of another thread re-reserving the range before this function is done with it. void os::release_memory(char* addr, size_t bytes) { assert_nonempty_range(addr, bytes); - bool res; - if (MemTracker::enabled()) { - MemTracker::NmtVirtualMemoryLocker nvml; - res = pd_release_memory(addr, bytes); - if (res) { - MemTracker::record_virtual_memory_release(addr, bytes); - } - } else { - res = pd_release_memory(addr, bytes); - } - if (!res) { + MemTracker::record_virtual_memory_release(addr, bytes); + if (!pd_release_memory(addr, bytes)) { fatal("Failed to release " RANGEFMT, RANGEFMTARGS(addr, bytes)); } log_debug(os, map)("Released " RANGEFMT, RANGEFMTARGS(addr, bytes)); @@ -2402,17 +2381,8 @@ char* os::map_memory(int fd, const char* file_name, size_t file_offset, } void os::unmap_memory(char *addr, size_t bytes) { - bool result; - if (MemTracker::enabled()) { - MemTracker::NmtVirtualMemoryLocker nvml; - result = pd_unmap_memory(addr, bytes); - if (result) { - MemTracker::record_virtual_memory_release(addr, bytes); - } - } else { - result = pd_unmap_memory(addr, bytes); - } - if (!result) { + MemTracker::record_virtual_memory_release(addr, bytes); + if (!pd_unmap_memory(addr, bytes)) { fatal("Failed to unmap memory " RANGEFMT, RANGEFMTARGS(addr, bytes)); } } diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index e185188384f..c883b828456 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -454,7 +454,7 @@ class os: AllStatic { static size_t align_down_vm_page_size(size_t size) { return align_down(size, os::vm_page_size()); } // The set of page sizes which the VM is allowed to use (may be a subset of - // the page sizes actually available on the platform). + // the page sizes actually available on the platform). static const PageSizes& page_sizes() { return _page_sizes; } // Returns the page size to use for a region of memory. @@ -721,6 +721,8 @@ class os: AllStatic { static int open(const char *path, int oflag, int mode); static FILE* fdopen(int fd, const char* mode); static FILE* fopen(const char* path, const char* mode); + static int64_t ftell(FILE* file); + static int fseek(FILE* file, int64_t offset, int whence); static jlong lseek(int fd, jlong offset, int whence); static bool file_exists(const char* file); @@ -893,6 +895,9 @@ class os: AllStatic { static void print_date_and_time(outputStream* st, char* buf, size_t buflen); static void print_elapsed_time(outputStream* st, double time); + // Prints the number of open file descriptors for the current process + static void print_open_file_descriptors(outputStream* st); + static void print_user_info(outputStream* st); static void print_active_locale(outputStream* st); diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index ae3835dd344..917ff5b4545 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -82,6 +82,7 @@ #include "utilities/copy.hpp" #include "utilities/dtrace.hpp" #include "utilities/events.hpp" +#include "utilities/exceptions.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/hashTable.hpp" #include "utilities/macros.hpp" @@ -931,7 +932,7 @@ void SharedRuntime::throw_StackOverflowError_common(JavaThread* current, bool de // bindings. current->clear_scopedValueBindings(); // Increment counter for hs_err file reporting - AtomicAccess::inc(&Exceptions::_stack_overflow_errors); + Exceptions::increment_stack_overflow_errors(); throw_and_post_jvmti_exception(current, exception); } diff --git a/src/hotspot/share/runtime/stubDeclarations.hpp b/src/hotspot/share/runtime/stubDeclarations.hpp index c478eda3e7c..7dc0f2d2bed 100644 --- a/src/hotspot/share/runtime/stubDeclarations.hpp +++ b/src/hotspot/share/runtime/stubDeclarations.hpp @@ -667,16 +667,6 @@ do_entry(initial, dcbrt, dcbrt, dcbrt) \ do_stub(initial, fmod) \ do_entry(initial, fmod, fmod, fmod) \ - /* following generic entries should really be x86_32 only */ \ - do_stub(initial, dlibm_sin_cos_huge) \ - do_entry(initial, dlibm_sin_cos_huge, dlibm_sin_cos_huge, \ - dlibm_sin_cos_huge) \ - do_stub(initial, dlibm_reduce_pi04l) \ - do_entry(initial, dlibm_reduce_pi04l, dlibm_reduce_pi04l, \ - dlibm_reduce_pi04l) \ - do_stub(initial, dlibm_tan_cot_huge) \ - do_entry(initial, dlibm_tan_cot_huge, dlibm_tan_cot_huge, \ - dlibm_tan_cot_huge) \ /* merge in stubs and entries declared in arch header */ \ STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, do_arch_blob, \ do_arch_entry, do_arch_entry_init) \ diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index 8ea5fe31145..e3293f94eeb 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -1416,7 +1416,11 @@ void ObjectSynchronizer::chk_in_use_entry(ObjectMonitor* n, outputStream* out, } const markWord mark = obj->mark(); - if (!mark.has_monitor()) { + // Note: When using ObjectMonitorTable we may observe an intermediate state, + // where the monitor is globally visible, but no thread has yet transitioned + // the markWord. To avoid reporting a false positive during this transition, we + // skip the `!mark.has_monitor()` test if we are using the ObjectMonitorTable. + if (!UseObjectMonitorTable && !mark.has_monitor()) { out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor's " "object does not think it has a monitor: obj=" INTPTR_FORMAT ", mark=" INTPTR_FORMAT, p2i(n), diff --git a/src/hotspot/share/runtime/threadSMR.cpp b/src/hotspot/share/runtime/threadSMR.cpp index 418f7707118..4c68648fec8 100644 --- a/src/hotspot/share/runtime/threadSMR.cpp +++ b/src/hotspot/share/runtime/threadSMR.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -726,7 +726,8 @@ JavaThread* ThreadsList::find_JavaThread_from_java_tid(jlong java_tid) const { } } } - } else if (!thread->is_exiting()) { + } else if (includes(thread) && !thread->is_exiting()) { + // The thread is protected by this list and has not yet exited return thread; } return nullptr; @@ -883,7 +884,7 @@ void ThreadsSMRSupport::add_thread(JavaThread *thread){ ThreadsList *old_list = xchg_java_thread_list(new_list); free_list(old_list); - if (ThreadIdTable::is_initialized()) { + if (ThreadIdTable::is_initialized_acquire()) { jlong tid = SharedRuntime::get_java_tid(thread); ThreadIdTable::add_thread(tid, thread); } diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp index f7f755a37b3..442b68e596a 100644 --- a/src/hotspot/share/runtime/threads.cpp +++ b/src/hotspot/share/runtime/threads.cpp @@ -1127,7 +1127,7 @@ void Threads::remove(JavaThread* p, bool is_daemon) { ConditionalMutexLocker throttle_ml(ThreadsLockThrottle_lock, UseThreadsLockThrottleLock); MonitorLocker ml(Threads_lock); - if (ThreadIdTable::is_initialized()) { + if (ThreadIdTable::is_initialized_acquire()) { // This cleanup must be done before the current thread's GC barrier // is detached since we need to touch the threadObj oop. jlong tid = SharedRuntime::get_java_tid(p); diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 9796b990b2a..93e0ff2f3b6 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -335,7 +335,6 @@ nonstatic_field(ThreadLocalAllocBuffer, _pf_top, HeapWord*) \ nonstatic_field(ThreadLocalAllocBuffer, _desired_size, size_t) \ nonstatic_field(ThreadLocalAllocBuffer, _refill_waste_limit, size_t) \ - static_field(ThreadLocalAllocBuffer, _reserve_for_allocation_prefetch, int) \ static_field(ThreadLocalAllocBuffer, _target_refills, unsigned) \ nonstatic_field(ThreadLocalAllocBuffer, _number_of_refills, unsigned) \ nonstatic_field(ThreadLocalAllocBuffer, _refill_waste, unsigned) \ diff --git a/src/hotspot/share/sanitizers/address.cpp b/src/hotspot/share/sanitizers/address.cpp index 7d129feab0a..26de106ebcb 100644 --- a/src/hotspot/share/sanitizers/address.cpp +++ b/src/hotspot/share/sanitizers/address.cpp @@ -30,6 +30,7 @@ #include "utilities/globalDefinitions.hpp" #include "utilities/vmError.hpp" +#ifndef _WINDOWS #include #include @@ -118,4 +119,11 @@ void Asan::report(outputStream* st) { } } +#else // defined windows + +void Asan::initialize() {} +bool Asan::had_error() { return false; } +void Asan::report(outputStream* st) {} +#endif // ifndef _WINDOWS + #endif // ADDRESS_SANITIZER diff --git a/src/hotspot/share/services/management.cpp b/src/hotspot/share/services/management.cpp index 09277e16479..664fb5a8ef3 100644 --- a/src/hotspot/share/services/management.cpp +++ b/src/hotspot/share/services/management.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -1146,6 +1146,7 @@ JVM_ENTRY(jint, jmm_GetThreadInfo(JNIEnv *env, jlongArray ids, jint maxDepth, jo // create dummy snapshot dump_result.add_thread_snapshot(); } else { + assert(dump_result.t_list()->includes(jt), "Must be protected"); dump_result.add_thread_snapshot(jt); } } diff --git a/src/hotspot/share/services/threadIdTable.cpp b/src/hotspot/share/services/threadIdTable.cpp index 1604927a0ac..585cc6377ec 100644 --- a/src/hotspot/share/services/threadIdTable.cpp +++ b/src/hotspot/share/services/threadIdTable.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2019, 2026, 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 @@ -23,7 +23,7 @@ */ #include "classfile/javaClasses.inline.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/handles.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaThread.inline.hpp" #include "runtime/threadSMR.hpp" @@ -44,7 +44,7 @@ static ThreadIdTableHash* volatile _local_table = nullptr; static volatile size_t _current_size = 0; static volatile size_t _items_count = 0; -volatile bool ThreadIdTable::_is_initialized = false; +Atomic ThreadIdTable::_is_initialized {false}; volatile bool ThreadIdTable::_has_work = false; class ThreadIdTableEntry : public CHeapObj { @@ -81,24 +81,25 @@ class ThreadIdTableConfig : public AllStatic { // Lazily creates the table and populates it with the given // thread list void ThreadIdTable::lazy_initialize(const ThreadsList *threads) { - if (!_is_initialized) { + if (!_is_initialized.load_acquire()) { { // There is no obvious benefit in allowing the thread table // to be concurrently populated during initialization. MutexLocker ml(ThreadIdTableCreate_lock); - if (_is_initialized) { + if (_is_initialized.load_relaxed()) { return; } create_table(threads->length()); - _is_initialized = true; + _is_initialized.release_store(true); } + for (uint i = 0; i < threads->length(); i++) { JavaThread* thread = threads->thread_at(i); - oop tobj = thread->threadObj(); + Handle tobj = Handle(JavaThread::current(), thread->threadObj()); if (tobj != nullptr) { - jlong java_tid = java_lang_Thread::thread_id(tobj); MutexLocker ml(Threads_lock); if (!thread->is_exiting()) { + jlong java_tid = java_lang_Thread::thread_id(tobj()); // Must be inside the lock to ensure that we don't add a thread to the table // that has just passed the removal point in Threads::remove(). add_thread(java_tid, thread); @@ -211,7 +212,7 @@ public: }; void ThreadIdTable::do_concurrent_work(JavaThread* jt) { - assert(_is_initialized, "Thread table is not initialized"); + assert(_is_initialized.load_relaxed(), "Thread table is not initialized"); _has_work = false; double load_factor = get_load_factor(); log_debug(thread, table)("Concurrent work, load factor: %g", load_factor); @@ -221,7 +222,8 @@ void ThreadIdTable::do_concurrent_work(JavaThread* jt) { } JavaThread* ThreadIdTable::add_thread(jlong tid, JavaThread* java_thread) { - assert(_is_initialized, "Thread table is not initialized"); + assert(Threads_lock->owned_by_self(), "Must hold Threads_lock"); + assert(_is_initialized.load_relaxed(), "Thread table is not initialized"); Thread* thread = Thread::current(); ThreadIdTableLookup lookup(tid); ThreadGet tg; @@ -240,7 +242,7 @@ JavaThread* ThreadIdTable::add_thread(jlong tid, JavaThread* java_thread) { } JavaThread* ThreadIdTable::find_thread_by_tid(jlong tid) { - assert(_is_initialized, "Thread table is not initialized"); + assert(_is_initialized.load_relaxed(), "Thread table is not initialized"); Thread* thread = Thread::current(); ThreadIdTableLookup lookup(tid); ThreadGet tg; @@ -249,7 +251,8 @@ JavaThread* ThreadIdTable::find_thread_by_tid(jlong tid) { } bool ThreadIdTable::remove_thread(jlong tid) { - assert(_is_initialized, "Thread table is not initialized"); + assert(Threads_lock->owned_by_self(), "Must hold Threads_lock"); + assert(_is_initialized.load_relaxed(), "Thread table is not initialized"); Thread* thread = Thread::current(); ThreadIdTableLookup lookup(tid); return _local_table->remove(thread, lookup); diff --git a/src/hotspot/share/services/threadIdTable.hpp b/src/hotspot/share/services/threadIdTable.hpp index 15dfb89d670..256484cce2d 100644 --- a/src/hotspot/share/services/threadIdTable.hpp +++ b/src/hotspot/share/services/threadIdTable.hpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2019, 2026, 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 @@ -26,6 +26,7 @@ #define SHARE_SERVICES_THREADIDTABLE_HPP #include "memory/allStatic.hpp" +#include "runtime/atomic.hpp" class JavaThread; class ThreadsList; @@ -34,13 +35,15 @@ class ThreadIdTableConfig; class ThreadIdTable : public AllStatic { friend class ThreadIdTableConfig; - static volatile bool _is_initialized; + static Atomic _is_initialized; static volatile bool _has_work; public: // Initialization static void lazy_initialize(const ThreadsList* threads); - static bool is_initialized() { return _is_initialized; } + static bool is_initialized_acquire() { + return _is_initialized.load_acquire(); + } // Lookup and list management static JavaThread* find_thread_by_tid(jlong tid); diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp index 504b923237e..e8533e29460 100644 --- a/src/hotspot/share/utilities/debug.cpp +++ b/src/hotspot/share/utilities/debug.cpp @@ -40,7 +40,7 @@ #include "nmt/memTracker.hpp" #include "oops/klass.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/flags/flagSetting.hpp" #include "runtime/frame.inline.hpp" #include "runtime/handles.inline.hpp" @@ -80,7 +80,7 @@ static char g_dummy; char* g_assert_poison = &g_dummy; const char* g_assert_poison_read_only = &g_dummy; -static intx g_asserting_thread = 0; +static Atomic g_asserting_thread{0}; #endif // CAN_SHOW_REGISTERS_ON_ASSERT int DebuggingContext::_enabled = 0; // Initially disabled. @@ -193,7 +193,7 @@ void report_vm_error(const char* file, int line, const char* error_msg, const ch const void* siginfo = nullptr; #ifdef CAN_SHOW_REGISTERS_ON_ASSERT - if (os::current_thread_id() == g_asserting_thread) { + if (os::current_thread_id() == g_asserting_thread.load_relaxed()) { context = os::get_saved_assert_context(&siginfo); } #endif // CAN_SHOW_REGISTERS_ON_ASSERT @@ -220,7 +220,7 @@ void report_fatal(VMErrorType error_type, const char* file, int line, const char const void* siginfo = nullptr; #ifdef CAN_SHOW_REGISTERS_ON_ASSERT - if (os::current_thread_id() == g_asserting_thread) { + if (os::current_thread_id() == g_asserting_thread.load_relaxed()) { context = os::get_saved_assert_context(&siginfo); } #endif // CAN_SHOW_REGISTERS_ON_ASSERT @@ -265,15 +265,15 @@ void report_untested(const char* file, int line, const char* message) { } void report_java_out_of_memory(const char* message) { - static int out_of_memory_reported = 0; + static Atomic out_of_memory_reported{false}; JFR_ONLY(Jfr::on_report_java_out_of_memory();) // A number of threads may attempt to report OutOfMemoryError at around the // same time. To avoid dumping the heap or executing the data collection - // commands multiple times we just do it once when the first threads reports + // commands multiple times we just do it once when the first thread that reports // the error. - if (AtomicAccess::cmpxchg(&out_of_memory_reported, 0, 1) == 0) { + if (out_of_memory_reported.compare_set(false, true)) { // create heap dump before OnOutOfMemoryError commands are executed if (HeapDumpOnOutOfMemoryError) { tty->print_cr("java.lang.OutOfMemoryError: %s", message); @@ -813,7 +813,7 @@ bool handle_assert_poison_fault(const void* ucVoid) { if (ucVoid != nullptr) { // Save context. const intx my_tid = os::current_thread_id(); - if (AtomicAccess::cmpxchg(&g_asserting_thread, (intx)0, my_tid) == 0) { + if (g_asserting_thread.compare_set(0, my_tid)) { os::save_assert_context(ucVoid); } } diff --git a/src/hotspot/share/utilities/dtrace_disabled.hpp b/src/hotspot/share/utilities/dtrace_disabled.hpp index 6cbd79326ac..99d7cbb2f75 100644 --- a/src/hotspot/share/utilities/dtrace_disabled.hpp +++ b/src/hotspot/share/utilities/dtrace_disabled.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, 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 @@ -683,6 +683,10 @@ #define HOTSPOT_JNI_GETMETHODID_ENTRY_ENABLED() 0 #define HOTSPOT_JNI_GETMETHODID_RETURN(arg0) #define HOTSPOT_JNI_GETMETHODID_RETURN_ENABLED() 0 +#define HOTSPOT_JNI_GETMODULE_ENTRY(arg0, arg1) +#define HOTSPOT_JNI_GETMODULE_ENTRY_ENABLED() 0 +#define HOTSPOT_JNI_GETMODULE_RETURN(arg0) +#define HOTSPOT_JNI_GETMODULE_RETURN_ENABLED() 0 #define HOTSPOT_JNI_GETOBJECTARRAYELEMENT_ENTRY(arg0, arg1, arg2) #define HOTSPOT_JNI_GETOBJECTARRAYELEMENT_ENTRY_ENABLED() 0 #define HOTSPOT_JNI_GETOBJECTARRAYELEMENT_RETURN(arg0) @@ -811,6 +815,10 @@ #define HOTSPOT_JNI_ISSAMEOBJECT_ENTRY_ENABLED() 0 #define HOTSPOT_JNI_ISSAMEOBJECT_RETURN(arg0) #define HOTSPOT_JNI_ISSAMEOBJECT_RETURN_ENABLED() 0 +#define HOTSPOT_JNI_ISVIRTUALTHREAD_ENTRY(arg0, arg1) +#define HOTSPOT_JNI_ISVIRTUALTHREAD_ENTRY_ENABLED() 0 +#define HOTSPOT_JNI_ISVIRTUALTHREAD_RETURN(arg0) +#define HOTSPOT_JNI_ISVIRTUALTHREAD_RETURN_ENABLED() 0 #define HOTSPOT_JNI_MONITORENTER_ENTRY(arg0, arg1) #define HOTSPOT_JNI_MONITORENTER_ENTRY_ENABLED() 0 #define HOTSPOT_JNI_MONITORENTER_RETURN(arg0) @@ -1080,12 +1088,6 @@ #define HOTSPOT_JNI_UNREGISTERNATIVES_RETURN(arg0) #define HOTSPOT_JNI_UNREGISTERNATIVES_RETURN_ENABLED() 0 -/* Modules */ -#define HOTSPOT_JNI_GETMODULE_ENTRY(arg0, arg1) -#define HOTSPOT_JNI_GETMODULE_ENTRY_ENABLED() 0 -#define HOTSPOT_JNI_GETMODULE_RETURN(arg0) -#define HOTSPOT_JNI_GETMODULE_RETURN_ENABLED() - #else /* !defined(DTRACE_ENABLED) */ #error This file should only be included when dtrace is not enabled #endif /* !defined(DTRACE_ENABLED) */ diff --git a/src/hotspot/share/utilities/events.cpp b/src/hotspot/share/utilities/events.cpp index 6adb5311cb5..d2b8e7ba5da 100644 --- a/src/hotspot/share/utilities/events.cpp +++ b/src/hotspot/share/utilities/events.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -26,14 +26,15 @@ #include "memory/allocation.inline.hpp" #include "oops/instanceKlass.hpp" #include "oops/symbol.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/javaThread.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/osThread.hpp" #include "runtime/timer.hpp" #include "utilities/events.hpp" -EventLog* Events::_logs = nullptr; +static Atomic event_logs_list{}; + StringEventLog* Events::_messages = nullptr; StringEventLog* Events::_memprotect_messages = nullptr; StringEventLog* Events::_nmethod_flush_messages = nullptr; @@ -51,15 +52,15 @@ EventLog::EventLog() { // but use lock free add because there are some events that are created later. EventLog* old_head; do { - old_head = AtomicAccess::load(&Events::_logs); + old_head = event_logs_list.load_relaxed(); _next = old_head; - } while (AtomicAccess::cmpxchg(&Events::_logs, old_head, this) != old_head); + } while (!event_logs_list.compare_set(old_head, this)); } // For each registered event logger, print out the current contents of // the buffer. void Events::print_all(outputStream* out, int max) { - EventLog* log = AtomicAccess::load(&Events::_logs); + EventLog* log = event_logs_list.load_relaxed(); while (log != nullptr) { log->print_log_on(out, max); log = log->next(); @@ -68,7 +69,7 @@ void Events::print_all(outputStream* out, int max) { // Print a single event log specified by name. void Events::print_one(outputStream* out, const char* log_name, int max) { - EventLog* log = AtomicAccess::load(&Events::_logs); + EventLog* log = event_logs_list.load_relaxed(); int num_printed = 0; while (log != nullptr) { if (log->matches_name_or_handle(log_name)) { @@ -81,7 +82,7 @@ void Events::print_one(outputStream* out, const char* log_name, int max) { if (num_printed == 0) { out->print_cr("The name \"%s\" did not match any known event log. " "Valid event log names are:", log_name); - EventLog* log = AtomicAccess::load(&Events::_logs); + EventLog* log = event_logs_list.load_relaxed(); while (log != nullptr) { log->print_names(out); out->cr(); diff --git a/src/hotspot/share/utilities/events.hpp b/src/hotspot/share/utilities/events.hpp index cbbed7232fb..0d1c08548ff 100644 --- a/src/hotspot/share/utilities/events.hpp +++ b/src/hotspot/share/utilities/events.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -214,10 +214,7 @@ class ExceptionsEventLog : public ExtendedStringEventLog { class Events : AllStatic { - friend class EventLog; - private: - static EventLog* _logs; // A log for generic messages that aren't well categorized. static StringEventLog* _messages; diff --git a/src/hotspot/share/utilities/exceptions.cpp b/src/hotspot/share/utilities/exceptions.cpp index b54474ea6d6..4455ab801cb 100644 --- a/src/hotspot/share/utilities/exceptions.cpp +++ b/src/hotspot/share/utilities/exceptions.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -32,7 +32,6 @@ #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/handles.inline.hpp" #include "runtime/init.hpp" #include "runtime/java.hpp" @@ -203,7 +202,7 @@ void Exceptions::_throw(JavaThread* thread, const char* file, int line, Handle h } if (h_exception->is_a(vmClasses::LinkageError_klass())) { - AtomicAccess::inc(&_linkage_errors, memory_order_relaxed); + _linkage_errors.add_then_fetch(1, memory_order_relaxed); } assert(h_exception->is_a(vmClasses::Throwable_klass()), "exception is not a subclass of java/lang/Throwable"); @@ -268,6 +267,10 @@ void Exceptions::_throw_cause(JavaThread* thread, const char* file, int line, Sy } +void Exceptions::increment_stack_overflow_errors() { + Exceptions::_stack_overflow_errors.add_then_fetch(1, memory_order_relaxed); +} + void Exceptions::throw_stack_overflow_exception(JavaThread* THREAD, const char* file, int line, const methodHandle& method) { Handle exception; if (!THREAD->has_pending_exception()) { @@ -279,7 +282,7 @@ void Exceptions::throw_stack_overflow_exception(JavaThread* THREAD, const char* java_lang_Throwable::fill_in_stack_trace(exception, method); } // Increment counter for hs_err file reporting - AtomicAccess::inc(&Exceptions::_stack_overflow_errors, memory_order_relaxed); + increment_stack_overflow_errors(); } else { // if prior exception, throw that one instead exception = Handle(THREAD, THREAD->pending_exception()); @@ -518,20 +521,20 @@ void Exceptions::wrap_dynamic_exception(bool is_indy, JavaThread* THREAD) { } // Exception counting for hs_err file -volatile int Exceptions::_stack_overflow_errors = 0; -volatile int Exceptions::_linkage_errors = 0; -volatile int Exceptions::_out_of_memory_error_java_heap_errors = 0; -volatile int Exceptions::_out_of_memory_error_metaspace_errors = 0; -volatile int Exceptions::_out_of_memory_error_class_metaspace_errors = 0; +Atomic Exceptions::_stack_overflow_errors{0}; +Atomic Exceptions::_linkage_errors{0}; +Atomic Exceptions::_out_of_memory_error_java_heap_errors{0}; +Atomic Exceptions::_out_of_memory_error_metaspace_errors{0}; +Atomic Exceptions::_out_of_memory_error_class_metaspace_errors{0}; void Exceptions::count_out_of_memory_exceptions(Handle exception) { if (Universe::is_out_of_memory_error_metaspace(exception())) { - AtomicAccess::inc(&_out_of_memory_error_metaspace_errors, memory_order_relaxed); + _out_of_memory_error_metaspace_errors.add_then_fetch(1, memory_order_relaxed); } else if (Universe::is_out_of_memory_error_class_metaspace(exception())) { - AtomicAccess::inc(&_out_of_memory_error_class_metaspace_errors, memory_order_relaxed); + _out_of_memory_error_class_metaspace_errors.add_then_fetch(1, memory_order_relaxed); } else { - // everything else reported as java heap OOM - AtomicAccess::inc(&_out_of_memory_error_java_heap_errors, memory_order_relaxed); + // everything else reported as java heap OOM + _out_of_memory_error_java_heap_errors.add_then_fetch(1, memory_order_relaxed); } } @@ -542,19 +545,24 @@ static void print_oom_count(outputStream* st, const char *err, int count) { } bool Exceptions::has_exception_counts() { - return (_stack_overflow_errors + _out_of_memory_error_java_heap_errors + - _out_of_memory_error_metaspace_errors + _out_of_memory_error_class_metaspace_errors) > 0; + return (_stack_overflow_errors.load_relaxed() + + _out_of_memory_error_java_heap_errors.load_relaxed() + + _out_of_memory_error_metaspace_errors.load_relaxed() + + _out_of_memory_error_class_metaspace_errors.load_relaxed()) > 0; } void Exceptions::print_exception_counts_on_error(outputStream* st) { - print_oom_count(st, "java_heap_errors", _out_of_memory_error_java_heap_errors); - print_oom_count(st, "metaspace_errors", _out_of_memory_error_metaspace_errors); - print_oom_count(st, "class_metaspace_errors", _out_of_memory_error_class_metaspace_errors); - if (_stack_overflow_errors > 0) { - st->print_cr("StackOverflowErrors=%d", _stack_overflow_errors); + print_oom_count(st, "java_heap_errors", + _out_of_memory_error_java_heap_errors.load_relaxed()); + print_oom_count(st, "metaspace_errors", + _out_of_memory_error_metaspace_errors.load_relaxed()); + print_oom_count(st, "class_metaspace_errors", + _out_of_memory_error_class_metaspace_errors.load_relaxed()); + if (_stack_overflow_errors.load_relaxed() > 0) { + st->print_cr("StackOverflowErrors=%d", _stack_overflow_errors.load_relaxed()); } - if (_linkage_errors > 0) { - st->print_cr("LinkageErrors=%d", _linkage_errors); + if (_linkage_errors.load_relaxed() > 0) { + st->print_cr("LinkageErrors=%d", _linkage_errors.load_relaxed()); } } diff --git a/src/hotspot/share/utilities/exceptions.hpp b/src/hotspot/share/utilities/exceptions.hpp index e76a0041d20..0299dedf2e6 100644 --- a/src/hotspot/share/utilities/exceptions.hpp +++ b/src/hotspot/share/utilities/exceptions.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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,6 +27,7 @@ #include "memory/allocation.hpp" #include "oops/oopsHierarchy.hpp" +#include "runtime/atomic.hpp" #include "utilities/ostream.hpp" #include "utilities/sizes.hpp" @@ -113,12 +114,16 @@ class Exceptions { static bool special_exception(JavaThread* thread, const char* file, int line, Handle exception, Symbol* name = nullptr, const char* message = nullptr); // Count out of memory errors that are interesting in error diagnosis - static volatile int _out_of_memory_error_java_heap_errors; - static volatile int _out_of_memory_error_metaspace_errors; - static volatile int _out_of_memory_error_class_metaspace_errors; + static Atomic _out_of_memory_error_java_heap_errors; + static Atomic _out_of_memory_error_metaspace_errors; + static Atomic _out_of_memory_error_class_metaspace_errors; // Count linkage errors - static volatile int _linkage_errors; + static Atomic _linkage_errors; + + // Count stack overflow errors. + static Atomic _stack_overflow_errors; + public: // this enum is defined to indicate whether it is safe to // ignore the encoding scheme of the original message string. @@ -179,10 +184,9 @@ class Exceptions { static void wrap_dynamic_exception(bool is_indy, JavaThread* thread); - // Exception counting for error files of interesting exceptions that may have - // caused a problem for the jvm - static volatile int _stack_overflow_errors; - + // Exception counting of interesting exceptions that may have caused a + // problem for the JVM, for reporting in the hs_err file. + static void increment_stack_overflow_errors(); static bool has_exception_counts(); static void count_out_of_memory_exceptions(Handle exception); static void print_exception_counts_on_error(outputStream* st); diff --git a/src/hotspot/share/utilities/filterQueue.hpp b/src/hotspot/share/utilities/filterQueue.hpp index 141c40f09c8..ea47f07b7b8 100644 --- a/src/hotspot/share/utilities/filterQueue.hpp +++ b/src/hotspot/share/utilities/filterQueue.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -26,7 +26,7 @@ #define SHARE_UTILITIES_FILTERQUEUE_HPP #include "memory/allocation.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" // The FilterQueue is FIFO with the ability to skip over queued items. // The skipping is controlled by using a filter when popping. @@ -42,9 +42,9 @@ class FilterQueue { E _data; }; - Node* _first; + Atomic _first; Node* load_first() { - return AtomicAccess::load_acquire(&_first); + return _first.load_acquire(); } static bool match_all(E d) { return true; } diff --git a/src/hotspot/share/utilities/filterQueue.inline.hpp b/src/hotspot/share/utilities/filterQueue.inline.hpp index 18b40b81c6c..7fa1bc94b7b 100644 --- a/src/hotspot/share/utilities/filterQueue.inline.hpp +++ b/src/hotspot/share/utilities/filterQueue.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -37,7 +37,7 @@ void FilterQueue::push(E data) { while (true){ head = load_first(); insnode->_next = head; - if (AtomicAccess::cmpxchg(&_first, head, insnode) == head) { + if (_first.compare_set(head, insnode)) { break; } yield.wait(); @@ -91,7 +91,7 @@ E FilterQueue::pop(MATCH_FUNC& match_func) { if (match_prev == nullptr) { // Working on first - if (AtomicAccess::cmpxchg(&_first, match, match->_next) == match) { + if (_first.compare_set(match, match->_next)) { E ret = match->_data; delete match; return ret; diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 9560d863a2c..40691de518e 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -1072,18 +1072,26 @@ const intptr_t NoBits = 0; // no bits set in a word const jlong NoLongBits = 0; // no bits set in a long const intptr_t OneBit = 1; // only right_most bit set in a word -// get a word with the n.th or the right-most or left-most n bits set -// (note: #define used only so that they can be used in enum constant definitions) -#define nth_bit(n) (((n) >= BitsPerWord) ? 0 : (OneBit << (n))) -#define right_n_bits(n) (nth_bit(n) - 1) - -// same as nth_bit(n), but allows handing in a type as template parameter. Allows -// us to use nth_bit with 64-bit types on 32-bit platforms -template inline T nth_bit_typed(int n) { - return ((T)1) << n; +// Return a value of type T with the n.th bit set and all other bits zero. +// T must be an integral or enum type. n must be non-negative. If n is at +// least the bitwise size of T then all bits in the result are zero. +template +constexpr T nth_bit(int n) { + assert(n >= 0, "n must be non-negative"); + using U = std::make_unsigned_t; + constexpr size_t size = sizeof(U) * BitsPerByte; + return T((size_t(n) >= size) ? U(0) : (U(1) << n)); } -template inline T right_n_bits_typed(int n) { - return nth_bit_typed(n) - 1; + +// Return a value of type T with all bits below the n.th bit set and all +// other bits zero. T must be an integral or enum type. n must be +// non-negative. If n is at least the bitwise size of T then all bits in +// the result are set. +template +constexpr T right_n_bits(int n) { + assert(n >= 0, "n must be non-negative"); + using U = std::make_unsigned_t; + return T(nth_bit(n) - 1); } // bit-operations using a mask m diff --git a/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp b/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp index dfd6f2f1880..f106d325c68 100644 --- a/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp +++ b/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp @@ -69,7 +69,9 @@ inline int strncasecmp(const char *s1, const char *s2, size_t n) { // *not* the same as the C99 Annex K strtok_s. VS provides that function // under the name strtok_s_l. Make strtok_r a synonym so we can use that name // in shared code. -const auto strtok_r = strtok_s; +inline char* strtok_r(char* str, const char* delim, char** saveptr) { + return strtok_s(str, delim, saveptr); +} // VS doesn't provide POSIX macros S_ISFIFO or S_IFIFO. It doesn't even // provide _S_ISFIFO, per its usual naming convention for POSIX stuff. But it diff --git a/src/hotspot/share/utilities/intn_t.hpp b/src/hotspot/share/utilities/intn_t.hpp index 594e62a1694..1b8e7de652e 100644 --- a/src/hotspot/share/utilities/intn_t.hpp +++ b/src/hotspot/share/utilities/intn_t.hpp @@ -84,6 +84,7 @@ public: constexpr bool operator>(intn_t o) const { return int(*this) > int(o); } constexpr bool operator<=(intn_t o) const { return int(*this) <= int(o); } constexpr bool operator>=(intn_t o) const { return int(*this) >= int(o); } + constexpr intn_t operator>>(unsigned int s) const { return intn_t(int(*this) >> s); } }; template @@ -163,4 +164,35 @@ inline unsigned count_leading_zeros(uintn_t v) { return count_leading_zeros(v._v & uintn_t::_mask) - (32 - nbits); } +class HotSpotNumerics { +private: + template + static constexpr int type_width_impl(T value) { + // Count the number of 1s in `value`. We can't use population_count() from + // utilities/population_count.hpp, since it requires `std::is_integral`, which + // fails for `uintn_t`. Since this is a constexpr function, this function + // does not impose a runtime performance overhead. + return value == T(0) ? 0 : 1 + type_width_impl(value >> 1); + } + +public: + // Returns true if T is a signed type. We can't rely on std::is_signed + // because it returns false for intn_t, which is not a standard integral + // type. Instead, we check whether T(-1) is less than T(0). + template + static constexpr bool is_signed() { + return T(-1) < T(0); + } + + // Returns the bit width of the unsigned type T. We can't use sizeof() on T, + // since sizeof(uintn_t) returns the size of the underlying storage rather + // than the logical type width. So we instead compute the number of 1s in the + // maximum value. + template + static constexpr int type_width() { + static_assert(!is_signed(), "type_width requires an unsigned type"); + return type_width_impl(std::numeric_limits::max()); + } +}; + #endif // SHARE_UTILITIES_INTN_T_HPP diff --git a/src/hotspot/share/utilities/ostream.cpp b/src/hotspot/share/utilities/ostream.cpp index 5e339a700cb..ded233d48bf 100644 --- a/src/hotspot/share/utilities/ostream.cpp +++ b/src/hotspot/share/utilities/ostream.cpp @@ -611,15 +611,15 @@ void fileStream::write(const char* s, size_t len) { } } -long fileStream::fileSize() { - long size = -1; +int64_t fileStream::fileSize() { + int64_t size = -1; if (_file != nullptr) { - long pos = ::ftell(_file); + int64_t pos = os::ftell(_file); if (pos < 0) return pos; - if (::fseek(_file, 0, SEEK_END) == 0) { - size = ::ftell(_file); + if (os::fseek(_file, 0, SEEK_END) == 0) { + size = os::ftell(_file); } - ::fseek(_file, pos, SEEK_SET); + os::fseek(_file, pos, SEEK_SET); } return size; } diff --git a/src/hotspot/share/utilities/ostream.hpp b/src/hotspot/share/utilities/ostream.hpp index e971ac4d125..c3a4026f4d9 100644 --- a/src/hotspot/share/utilities/ostream.hpp +++ b/src/hotspot/share/utilities/ostream.hpp @@ -312,7 +312,7 @@ class fileStream : public outputStream { fclose(_file); _need_close = false; } - long fileSize(); + int64_t fileSize(); void flush(); }; diff --git a/src/hotspot/share/utilities/rbTree.hpp b/src/hotspot/share/utilities/rbTree.hpp index c522d787466..9c04ccbe9ab 100644 --- a/src/hotspot/share/utilities/rbTree.hpp +++ b/src/hotspot/share/utilities/rbTree.hpp @@ -429,7 +429,12 @@ public: void free(void* ptr); }; - +template +RBTreeOrdering rbtree_primitive_cmp(T a, T b) { // handy function + if (a < b) return RBTreeOrdering::LT; + if (a > b) return RBTreeOrdering::GT; + return RBTreeOrdering::EQ; +} template using RBTreeCHeap = RBTree>; diff --git a/src/hotspot/share/utilities/stringUtils.hpp b/src/hotspot/share/utilities/stringUtils.hpp index c3d21233808..66c8d30c7c0 100644 --- a/src/hotspot/share/utilities/stringUtils.hpp +++ b/src/hotspot/share/utilities/stringUtils.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -26,11 +26,7 @@ #define SHARE_UTILITIES_STRINGUTILS_HPP #include "memory/allStatic.hpp" - -#ifdef _WINDOWS - // strtok_s is the Windows thread-safe equivalent of POSIX strtok_r -# define strtok_r strtok_s -#endif +#include "utilities/globalDefinitions.hpp" class StringUtils : AllStatic { public: diff --git a/src/hotspot/share/utilities/tableStatistics.cpp b/src/hotspot/share/utilities/tableStatistics.cpp index 331652becd5..34d0969969a 100644 --- a/src/hotspot/share/utilities/tableStatistics.cpp +++ b/src/hotspot/share/utilities/tableStatistics.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -22,7 +22,6 @@ * */ -#include "runtime/atomicAccess.hpp" #include "runtime/os.hpp" #include "utilities/debug.hpp" #include "utilities/macros.hpp" @@ -42,7 +41,7 @@ TableRateStatistics::~TableRateStatistics() { }; void TableRateStatistics::add() { #if INCLUDE_JFR if (Jfr::is_recording()) { - AtomicAccess::inc(&_added_items); + _added_items.add_then_fetch(1u); } #endif } @@ -50,7 +49,7 @@ void TableRateStatistics::add() { void TableRateStatistics::remove() { #if INCLUDE_JFR if (Jfr::is_recording()) { - AtomicAccess::inc(&_removed_items); + _removed_items.add_then_fetch(1u); } #endif } @@ -61,8 +60,8 @@ void TableRateStatistics::stamp() { _added_items_stamp_prev = _added_items_stamp; _removed_items_stamp_prev = _removed_items_stamp; - _added_items_stamp = _added_items; - _removed_items_stamp = _removed_items; + _added_items_stamp = _added_items.load_relaxed(); + _removed_items_stamp = _removed_items.load_relaxed(); if (_time_stamp == 0) { _time_stamp = now - 1000000000; diff --git a/src/hotspot/share/utilities/tableStatistics.hpp b/src/hotspot/share/utilities/tableStatistics.hpp index d4fd3302922..95856114833 100644 --- a/src/hotspot/share/utilities/tableStatistics.hpp +++ b/src/hotspot/share/utilities/tableStatistics.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -26,6 +26,7 @@ #define SHARE_UTILITIES_TABLE_STATISTICS_HPP #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/numberSeq.hpp" @@ -35,8 +36,8 @@ class TableRateStatistics : public CHeapObj { friend class TableStatistics; private: - volatile size_t _added_items; - volatile size_t _removed_items; + Atomic _added_items; + Atomic _removed_items; jlong _time_stamp; double _seconds_stamp; diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index 6088eafbda4..48fae6868ab 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -43,7 +43,6 @@ #include "oops/compressedOops.hpp" #include "prims/whitebox.hpp" #include "runtime/arguments.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/flags/jvmFlag.hpp" #include "runtime/frame.inline.hpp" #include "runtime/init.hpp" @@ -86,12 +85,12 @@ bool VMError::coredump_status; char VMError::coredump_message[O_BUFLEN]; int VMError::_current_step; const char* VMError::_current_step_info; -volatile jlong VMError::_reporting_start_time = -1; -volatile bool VMError::_reporting_did_timeout = false; -volatile jlong VMError::_step_start_time = -1; -volatile bool VMError::_step_did_timeout = false; -volatile bool VMError::_step_did_succeed = false; -volatile intptr_t VMError::_first_error_tid = -1; +Atomic VMError::_reporting_start_time{-1}; +Atomic VMError::_reporting_did_timeout{false}; +Atomic VMError::_step_start_time{-1}; +Atomic VMError::_step_did_timeout{false}; +Atomic VMError::_step_did_succeed{false}; +Atomic VMError::_first_error_tid{-1}; int VMError::_id; const char* VMError::_message; char VMError::_detail_msg[1024]; @@ -105,8 +104,8 @@ int VMError::_lineno; size_t VMError::_size; const size_t VMError::_reattempt_required_stack_headroom = 64 * K; const intptr_t VMError::segfault_address = pd_segfault_address; -Thread* volatile VMError::_handshake_timed_out_thread = nullptr; -Thread* volatile VMError::_safepoint_timed_out_thread = nullptr; +Atomic VMError::_handshake_timed_out_thread{}; +Atomic VMError::_safepoint_timed_out_thread{}; // List of environment variables that should be reported in error log file. static const char* env_list[] = { @@ -248,7 +247,7 @@ bool VMError::can_reattempt_step(const char* &stop_reason) { return false; } - if (_step_did_timeout) { + if (_step_did_timeout.load_relaxed()) { stop_reason = "Step time limit reached"; return false; } @@ -543,12 +542,12 @@ static void report_vm_version(outputStream* st, char* buf, int buflen) { // Returns true if at least one thread reported a fatal error and fatal error handling is in process. bool VMError::is_error_reported() { - return _first_error_tid != -1; + return _first_error_tid.load_relaxed() != -1; } // Returns true if the current thread reported a fatal error. bool VMError::is_error_reported_in_current_thread() { - return _first_error_tid == os::current_thread_id(); + return _first_error_tid.load_relaxed() == os::current_thread_id(); } // Helper, return current timestamp for timeout handling. @@ -560,24 +559,24 @@ jlong VMError::get_current_timestamp() { void VMError::record_reporting_start_time() { const jlong now = get_current_timestamp(); - AtomicAccess::store(&_reporting_start_time, now); + _reporting_start_time.store_relaxed(now); } jlong VMError::get_reporting_start_time() { - return AtomicAccess::load(&_reporting_start_time); + return _reporting_start_time.load_relaxed(); } void VMError::record_step_start_time() { const jlong now = get_current_timestamp(); - AtomicAccess::store(&_step_start_time, now); + _step_start_time.store_relaxed(now); } jlong VMError::get_step_start_time() { - return AtomicAccess::load(&_step_start_time); + return _step_start_time.load_relaxed(); } void VMError::clear_step_start_time() { - return AtomicAccess::store(&_step_start_time, (jlong)0); + return _step_start_time.store_relaxed(0); } // This is the main function to report a fatal error. Only one thread can @@ -612,31 +611,31 @@ void VMError::report(outputStream* st, bool _verbose) { const char* stop_reattempt_reason = nullptr; # define BEGIN \ if (_current_step == 0) { \ - _step_did_succeed = false; \ + _step_did_succeed.store_relaxed(false); \ _current_step = __LINE__; \ { // [Begin logic] # define STEP_IF(s, cond) \ } \ - _step_did_succeed = true; \ + _step_did_succeed.store_relaxed(true); \ } \ if (_current_step < __LINE__) { \ - _step_did_succeed = false; \ + _step_did_succeed.store_relaxed(false); \ _current_step = __LINE__; \ _current_step_info = s; \ if ((cond)) { \ record_step_start_time(); \ - _step_did_timeout = false; + _step_did_timeout.store_relaxed(false); // [Step logic] # define STEP(s) STEP_IF(s, true) # define REATTEMPT_STEP_IF(s, cond) \ } \ - _step_did_succeed = true; \ + _step_did_succeed.store_relaxed(true); \ } \ - if (_current_step < __LINE__ && !_step_did_succeed) { \ + if (_current_step < __LINE__ && !_step_did_succeed.load_relaxed()) { \ _current_step = __LINE__; \ _current_step_info = s; \ const bool cond_value = (cond); \ @@ -650,7 +649,7 @@ void VMError::report(outputStream* st, bool _verbose) { # define END \ } \ - _step_did_succeed = true; \ + _step_did_succeed.store_relaxed(true); \ clear_step_start_time(); \ } @@ -1330,6 +1329,13 @@ void VMError::report(outputStream* st, bool _verbose) { STEP_IF("printing OS information", _verbose) os::print_os_info(st); st->cr(); +#ifdef __APPLE__ + // Avoid large stack allocation on Mac for FD count during signal-handling. + os::Bsd::print_open_file_descriptors(st, buf, sizeof(buf)); + st->cr(); +#else + os::print_open_file_descriptors(st); +#endif STEP_IF("printing CPU info", _verbose) os::print_cpu_info(st, buf, sizeof(buf)); @@ -1359,21 +1365,21 @@ void VMError::report(outputStream* st, bool _verbose) { void VMError::set_handshake_timed_out_thread(Thread* thread) { // Only preserve the first thread to time-out this way. The atomic operation ensures // visibility to the target thread. - AtomicAccess::replace_if_null(&_handshake_timed_out_thread, thread); + _handshake_timed_out_thread.compare_exchange(nullptr, thread); } void VMError::set_safepoint_timed_out_thread(Thread* thread) { // Only preserve the first thread to time-out this way. The atomic operation ensures // visibility to the target thread. - AtomicAccess::replace_if_null(&_safepoint_timed_out_thread, thread); + _safepoint_timed_out_thread.compare_exchange(nullptr, thread); } Thread* VMError::get_handshake_timed_out_thread() { - return AtomicAccess::load(&_handshake_timed_out_thread); + return _handshake_timed_out_thread.load_relaxed(); } Thread* VMError::get_safepoint_timed_out_thread() { - return AtomicAccess::load(&_safepoint_timed_out_thread); + return _safepoint_timed_out_thread.load_relaxed(); } // Report for the vm_info_cmd. This prints out the information above omitting @@ -1551,6 +1557,8 @@ void VMError::print_vm_info(outputStream* st) { os::print_os_info(st); st->cr(); + os::print_open_file_descriptors(st); + st->cr(); // STEP("printing CPU info") @@ -1708,8 +1716,7 @@ void VMError::report_and_die(int id, const char* message, const char* detail_fmt static bool log_done = false; // done saving error log intptr_t mytid = os::current_thread_id(); - if (_first_error_tid == -1 && - AtomicAccess::cmpxchg(&_first_error_tid, (intptr_t)-1, mytid) == -1) { + if (_first_error_tid.compare_set(-1, mytid)) { if (SuppressFatalErrorMessage) { os::abort(CreateCoredumpOnCrash); @@ -1756,7 +1763,7 @@ void VMError::report_and_die(int id, const char* message, const char* detail_fmt } else { // This is not the first error, see if it happened in a different thread // or in the same thread during error reporting. - if (_first_error_tid != mytid) { + if (_first_error_tid.load_relaxed() != mytid) { if (!SuppressFatalErrorMessage) { char msgbuf[64]; jio_snprintf(msgbuf, sizeof(msgbuf), @@ -1788,19 +1795,19 @@ void VMError::report_and_die(int id, const char* message, const char* detail_fmt st->cr(); // Timeout handling. - if (_step_did_timeout) { + if (_step_did_timeout.load_relaxed()) { // The current step had a timeout. Lets continue reporting with the next step. st->print_raw("[timeout occurred during error reporting in step \""); st->print_raw(_current_step_info); st->print_cr("\"] after " INT64_FORMAT " s.", (int64_t) - ((get_current_timestamp() - _step_start_time) / TIMESTAMP_TO_SECONDS_FACTOR)); - } else if (_reporting_did_timeout) { + ((get_current_timestamp() - get_step_start_time()) / TIMESTAMP_TO_SECONDS_FACTOR)); + } else if (_reporting_did_timeout.load_relaxed()) { // We hit ErrorLogTimeout. Reporting will stop altogether. Let's wrap things // up, the process is about to be stopped by the WatcherThread. st->print_cr("------ Timeout during error reporting after " INT64_FORMAT " s. ------", (int64_t) - ((get_current_timestamp() - _reporting_start_time) / TIMESTAMP_TO_SECONDS_FACTOR)); + ((get_current_timestamp() - get_reporting_start_time()) / TIMESTAMP_TO_SECONDS_FACTOR)); st->flush(); // Watcherthread is about to call os::die. Lets just wait. os::infinite_sleep(); @@ -2100,10 +2107,10 @@ bool VMError::check_timeout() { // Timestamp is stored in nanos. if (reporting_start_time > 0) { const jlong end = reporting_start_time + (jlong)ErrorLogTimeout * TIMESTAMP_TO_SECONDS_FACTOR; - if (end <= now && !_reporting_did_timeout) { + if (end <= now && !_reporting_did_timeout.load_relaxed()) { // We hit ErrorLogTimeout and we haven't interrupted the reporting // thread yet. - _reporting_did_timeout = true; + _reporting_did_timeout.store_relaxed(true); interrupt_reporting_thread(); return true; // global timeout } @@ -2119,10 +2126,10 @@ bool VMError::check_timeout() { const int max_step_timeout_secs = 5; const jlong timeout_duration = MAX2((jlong)max_step_timeout_secs, (jlong)ErrorLogTimeout * TIMESTAMP_TO_SECONDS_FACTOR / 4); const jlong end = step_start_time + timeout_duration; - if (end <= now && !_step_did_timeout) { + if (end <= now && !_step_did_timeout.load_relaxed()) { // The step timed out and we haven't interrupted the reporting // thread yet. - _step_did_timeout = true; + _step_did_timeout.store_relaxed(true); interrupt_reporting_thread(); return false; // (Not a global timeout) } diff --git a/src/hotspot/share/utilities/vmError.hpp b/src/hotspot/share/utilities/vmError.hpp index 04cea6de47c..b46ba208788 100644 --- a/src/hotspot/share/utilities/vmError.hpp +++ b/src/hotspot/share/utilities/vmError.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,6 +27,7 @@ #define SHARE_UTILITIES_VMERROR_HPP #include "memory/allStatic.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ostream.hpp" @@ -73,7 +74,7 @@ class VMError : public AllStatic { // Thread id of the first error. We must be able to handle native thread, // so use thread id instead of Thread* to identify thread. - static volatile intptr_t _first_error_tid; + static Atomic _first_error_tid; // Core dump status, false if we have been unable to write a core/minidump for some reason static bool coredump_status; @@ -85,16 +86,16 @@ class VMError : public AllStatic { // Timeout handling: // Timestamp at which error reporting started; -1 if no error reporting in progress. - static volatile jlong _reporting_start_time; + static Atomic _reporting_start_time; // Whether or not error reporting did timeout. - static volatile bool _reporting_did_timeout; + static Atomic _reporting_did_timeout; // Timestamp at which the last error reporting step started; -1 if no error reporting // in progress. - static volatile jlong _step_start_time; + static Atomic _step_start_time; // Whether or not the last error reporting step did timeout. - static volatile bool _step_did_timeout; + static Atomic _step_did_timeout; // Whether or not the last error reporting step did succeed. - static volatile bool _step_did_succeed; + static Atomic _step_did_succeed; // Install secondary signal handler to handle secondary faults during error reporting // (see VMError::crash_handler) @@ -143,8 +144,8 @@ class VMError : public AllStatic { static void clear_step_start_time(); // Handshake/safepoint timed out threads - static Thread* volatile _handshake_timed_out_thread; - static Thread* volatile _safepoint_timed_out_thread; + static Atomic _handshake_timed_out_thread; + static Atomic _safepoint_timed_out_thread; WINDOWS_ONLY([[noreturn]] static void raise_fail_fast(const void* exrecord, const void* context);) diff --git a/src/hotspot/share/utilities/zipLibrary.cpp b/src/hotspot/share/utilities/zipLibrary.cpp index 54875516a0f..ad6bb82d43f 100644 --- a/src/hotspot/share/utilities/zipLibrary.cpp +++ b/src/hotspot/share/utilities/zipLibrary.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -24,6 +24,7 @@ #include "jvm_io.h" #include "runtime/arguments.hpp" +#include "runtime/atomic.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/os.inline.hpp" #include "runtime/semaphore.inline.hpp" @@ -50,10 +51,10 @@ static ZIP_GZip_InitParams_t ZIP_GZip_InitParams = nullptr; static ZIP_GZip_Fully_t ZIP_GZip_Fully = nullptr; static void* _zip_handle = nullptr; -static bool _loaded = false; +static Atomic _loaded{false}; static inline bool is_loaded() { - return AtomicAccess::load_acquire(&_loaded); + return _loaded.load_acquire(); } static inline bool not_loaded() { @@ -111,7 +112,7 @@ static void load_zip_library(bool vm_exit_on_failure) { } store_function_pointers(&path[0], vm_exit_on_failure); - AtomicAccess::release_store(&_loaded, true); + _loaded.release_store(true); assert(is_loaded(), "invariant"); } diff --git a/src/java.base/aix/native/include/dl_info.h b/src/java.base/aix/native/include/dl_info.h new file mode 100644 index 00000000000..0ea10149300 --- /dev/null +++ b/src/java.base/aix/native/include/dl_info.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1996, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 SAP SE. 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. + */ + +#ifndef AIX_INCLUDE_DL_INFO_H +#define AIX_INCLUDE_DL_INFO_H + +/* struct for dladdr + * Differences between AIX dladdr and Linux dladdr: + * + * 1) Dl_info.dli_fbase: can never work, is not included in our struct + * A loaded image on AIX is divided in multiple segments, at least two + * (text and data) but potentially also far more. This is because the loader may + * load each member into an own segment, as for instance happens with the libC.a + * 2) Dl_info.dli_sname: This only works for code symbols (functions); for data, a + * zero-length string is returned (""). + * 3) Dl_info.dli_saddr: For code, this will return the entry point of the function, + * not the function descriptor. + */ + +typedef struct { + const char *dli_fname; /* file path of loaded library */ + const char *dli_sname; /* symbol name; "" if not known */ + void *dli_saddr; /* address of *entry* of function; not function descriptor; */ +} Dl_info; + +#endif diff --git a/src/java.base/aix/native/libjli/java_md_aix.h b/src/java.base/aix/native/libjli/java_md_aix.h index d319a1d6353..d63030fc65e 100644 --- a/src/java.base/aix/native/libjli/java_md_aix.h +++ b/src/java.base/aix/native/libjli/java_md_aix.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018 SAP SE. All rights reserved. + * Copyright (c) 2016, 2026 SAP SE. 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 @@ -37,12 +37,7 @@ * in the hotspot implementation which is not available at this place, though. */ -typedef struct { - const char *dli_fname; /* file path of loaded library */ - void *dli_fbase; /* unsupported */ - const char *dli_sname; /* unsupported */ - void *dli_saddr; /* unsupported */ -} Dl_info; +#include "dl_info.h" int dladdr(void *addr, Dl_info *info); diff --git a/src/java.base/share/classes/java/lang/Character.java b/src/java.base/share/classes/java/lang/Character.java index 33284d86e2d..158c3126ab3 100644 --- a/src/java.base/share/classes/java/lang/Character.java +++ b/src/java.base/share/classes/java/lang/Character.java @@ -67,9 +67,13 @@ import static java.lang.constant.ConstantDescs.DEFAULT_NAME; * Character information is based on the Unicode Standard, version 17.0. *

* The Java platform has supported different versions of the Unicode - * Standard over time. Upgrades to newer versions of the Unicode Standard - * occurred in the following Java releases, each indicating the new version: + * Standard over time. The following tables list the version of Unicode used + * in each Java release. Unless otherwise specified, all update releases in a + * given Java release family use the same Unicode version. * + * * * * @@ -78,26 +82,56 @@ import static java.lang.constant.ConstantDescs.DEFAULT_NAME; * * * + * + * + * + * + * + * + * + * + * + * + * + *
Shows Java releases and supported Unicode versions
Java release
Java SE 26Unicode 17.0
Java SE 25Unicode 16.0
Java SE 21Unicode 15.0
Java SE 17Unicode 13.0
Java SE 11Unicode 10.0
Java SE 8Unicode 6.2
+ *

+ * Show other Java releases + *

Java releases prior to Java SE 8 are listed only if they upgraded the + * Unicode version

+ * + * + * + * + * + * + * * * + * + * * * * * * * + * + * + * + * * * + * + * * * * * - * - * + * + * * * - * - * * * * @@ -110,6 +144,8 @@ import static java.lang.constant.ConstantDescs.DEFAULT_NAME; * * *
Shows other Java releases and supported Unicode + * versions
Java releaseUnicode version
Java SE 24Unicode 16.0
Java SE 23Unicode 15.1
Java SE 22Unicode 15.1
Java SE 20Unicode 15.0
Java SE 19Unicode 14.0
Java SE 18Unicode 13.0
Java SE 16Unicode 13.0
Java SE 15Unicode 13.0
Java SE 14Unicode 12.1
Java SE 13Unicode 12.1
Java SE 12Unicode 11.0
Java SE 11Unicode 10.0
Java SE 10Unicode 8.0
Java SE 9Unicode 8.0
Java SE 8Unicode 6.2
Java SE 7Unicode 6.0
Java SE 5.0Unicode 1.1.5
+ *
+ *

* Variations from these base Unicode versions, such as recognized appendixes, * are documented elsewhere. *

Unicode Character Representations

diff --git a/src/java.base/share/classes/java/lang/StringUTF16.java b/src/java.base/share/classes/java/lang/StringUTF16.java index 27b9ae54a8a..23de31a61b7 100644 --- a/src/java.base/share/classes/java/lang/StringUTF16.java +++ b/src/java.base/share/classes/java/lang/StringUTF16.java @@ -67,30 +67,61 @@ final class StringUTF16 { // Check the size of a UTF16-coded string // Throw an exception if out of range - static int newBytesLength(int len) { - if (len < 0) { - throw new NegativeArraySizeException(); - } - if (len >= MAX_LENGTH) { - throw new OutOfMemoryError("UTF16 String size is " + len + - ", should be less than " + MAX_LENGTH); - } + private static int newBytesLength(int len) { + checkBytesLength(len); return len << 1; } + /** + * Checks if the provided length is a valid UTF-16 string byte array length. + * + * @param length a UTF-16 string byte array length + * + * @throws NegativeArraySizeException if {@code length < 0} + * @throws OutOfMemoryError if {@code length > (Integer.MAX_VALUE / 2)} + */ + private static void checkBytesLength(int length) { + if (length < 0) { + throw new NegativeArraySizeException(); + } + if (length >= MAX_LENGTH) { + throw new OutOfMemoryError("UTF16 String size is " + length + + ", should be less than " + MAX_LENGTH); + } + } + + /** + * Writes the given code point to the specified position of the provided + * UTF-16 string byte array. + *

+ * WARNING: This method does not perform any input validations. + * + * @param val a UTF-16 string byte array + * @param index the index of the character to write the code point to + * @param c a code point + */ + // vmIntrinsics::_putCharStringU @IntrinsicCandidate - // intrinsic performs no bounds checks static void putChar(byte[] val, int index, int c) { - assert index >= 0 && index < length(val) : "Trusted caller missed bounds check"; + assert val != null && index >= 0 && index < length(val) : "Trusted caller violated input constraints"; index <<= 1; val[index++] = (byte)(c >> HI_BYTE_SHIFT); val[index] = (byte)(c >> LO_BYTE_SHIFT); } + /** + * {@return the code point at the the specified position of the provided + * UTF-16 string byte array} + *

+ * WARNING: This method does not perform any input validations. + * + * @param val a UTF-16 string byte array + * @param index the index of the character to get the code point from + */ + // vmIntrinsics::_getCharStringU @IntrinsicCandidate - // intrinsic performs no bounds checks static char getChar(byte[] val, int index) { - assert index >= 0 && index < length(val) : "Trusted caller missed bounds check"; + assert val != null && index >= 0 && index < length(val) : "Trusted caller violated input constraints"; index <<= 1; return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) | ((val[index] & 0xff) << LO_BYTE_SHIFT)); @@ -173,14 +204,27 @@ final class StringUTF16 { } /** - * {@return an encoded byte[] for the UTF16 characters in char[]} - * No checking is done on the characters, some may or may not be latin1. - * @param value a char array - * @param off an offset - * @param len a length + * {@return a UTF-16 string byte array produced by encoding the characters + * in the provided character array sub-range} + * + * @param value a character array to encode + * @param off the index of the character to start encoding from + * @param len the number of characters to encode + * + * @throws NegativeArraySizeException if {@code len < 0} + * @throws NullPointerException if {@code value} is null + * @throws OutOfMemoryError if {@code len > (Integer.MAX_VALUE / 2)} + * @throws StringIndexOutOfBoundsException if the sub-range is out of bounds */ - @IntrinsicCandidate static byte[] toBytes(char[] value, int off, int len) { + checkBytesLength(len); + String.checkBoundsOffCount(off, len, value.length); // Implicit null check on `value` + return toBytes0(value, off, len); + } + + // vmIntrinsics::_toBytesStringU + @IntrinsicCandidate + private static byte[] toBytes0(char[] value, int off, int len) { byte[] val = newBytesFor(len); for (int i = 0; i < len; i++) { putChar(val, i, value[off]); @@ -495,12 +539,28 @@ final class StringUTF16 { return result; } - @IntrinsicCandidate + /** + * Copies the specified sub-range of characters from a UTF-16 string byte + * array to the specified character array sub-range. + * + * @param value the source UTF-16 string byte array to copy from + * @param srcBegin the index (inclusive) of the first character in the source sub-range + * @param srcEnd the index (exclusive) of the last character in the source sub-range + * @param dst the target character array to copy to + * @param dstBegin the index (inclusive) of the first character in the target sub-range + * + * @throws NullPointerException if {@code value} or {@code dst} is null + * @throws StringIndexOutOfBoundsException if the sub-ranges are out of bounds + */ static void getChars(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) { - // We need a range check here because 'getChar' has no checks - if (srcBegin < srcEnd) { - String.checkBoundsOffCount(srcBegin, srcEnd - srcBegin, length(value)); - } + checkBoundsBeginEnd(srcBegin, srcEnd, value); // Implicit null check on `value` via `checkBoundsBeginEnd()` + String.checkBoundsOffCount(dstBegin, srcEnd - srcBegin, dst.length); // Implicit null check on `dst` + getChars0(value, srcBegin, srcEnd, dst, dstBegin); + } + + // vmIntrinsics::_getCharsStringU + @IntrinsicCandidate + private static void getChars0(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) { for (int i = srcBegin; i < srcEnd; i++) { dst[dstBegin++] = getChar(value, i); } @@ -721,7 +781,7 @@ final class StringUTF16 { return -StringLatin1.compareToCI_UTF16(other, value); } - public static int compareToFC_Latin1(byte[] value, byte[] other) { + static int compareToFC_Latin1(byte[] value, byte[] other) { return -StringLatin1.compareToFC_UTF16(other, value); } @@ -769,7 +829,7 @@ final class StringUTF16 { return 0; } - public static int compareToFC(byte[] value, byte[] other) { + static int compareToFC(byte[] value, byte[] other) { int tlast = length(value); int olast = length(other); int lim = Math.min(tlast, olast); @@ -1970,13 +2030,13 @@ final class StringUTF16 { } } - static final int MAX_LENGTH = Integer.MAX_VALUE >> 1; + private static final int MAX_LENGTH = Integer.MAX_VALUE >> 1; - static void checkIndex(int off, byte[] val) { + private static void checkIndex(int off, byte[] val) { String.checkIndex(off, length(val)); } - static void checkOffset(int off, byte[] val) { + private static void checkOffset(int off, byte[] val) { String.checkOffset(off, length(val)); } diff --git a/src/java.base/share/classes/java/lang/foreign/Linker.java b/src/java.base/share/classes/java/lang/foreign/Linker.java index cfa03090299..1a7d33266aa 100644 --- a/src/java.base/share/classes/java/lang/foreign/Linker.java +++ b/src/java.base/share/classes/java/lang/foreign/Linker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -813,25 +813,28 @@ public sealed interface Linker permits AbstractLinker { } /** - * {@return a linker option used to save portions of the execution state - * immediately after calling a foreign function associated with a - * downcall method handle, before it can be overwritten by the Java - * runtime, or read through conventional means} + * {@return a linker option used to initialize portions of the execution + * state immediately before, and save portions of the execution + * state immediately after calling a foreign function associated + * with a downcall method handle, before it can be overwritten by the + * Java runtime, or read through conventional means} *

- * Execution state is captured by a downcall method handle on invocation, by - * writing it to a native segment provided by the user to the downcall method - * handle. For this purpose, a downcall method handle linked with this option - * will feature an additional {@link MemorySegment} parameter directly following - * the target address, and optional {@link SegmentAllocator} parameters. This - * parameter, the capture state segment, represents the native segment - * into which the captured state is written. + * Execution state is initialized from, or saved to a native segment provided by + * the user to the downcall method handle. For this purpose, a downcall method + * handle linked with this option will feature an additional {@link MemorySegment} + * parameter directly following the target address, and optional {@link SegmentAllocator} + * parameters. This parameter, the capture state segment, represents the + * native segment from which the capture state is initialized, and into which the + * capture state is saved. *

* The capture state segment must have size and alignment compatible with the * layout returned by {@linkplain #captureStateLayout}. This layout is a struct * layout which has a named field for each captured value. *

- * Captured state can be retrieved from the capture state segment by constructing - * var handles from the {@linkplain #captureStateLayout capture state layout}. + * Captured state can be stored in, or retrieved from the capture state segment by + * constructing var handles from the {@linkplain #captureStateLayout capture state layout}. + * Some functions require this state the be initialized to a particular value before + * invoking the downcall. *

* The following example demonstrates the use of this linker option: * {@snippet lang = "java": @@ -843,6 +846,7 @@ public sealed interface Linker permits AbstractLinker { * VarHandle errnoHandle = capturedStateLayout.varHandle(PathElement.groupElement("errno")); * try (Arena arena = Arena.ofConfined()) { * MemorySegment capturedState = arena.allocate(capturedStateLayout); + * errnoHandle.set(capturedState, 0L, 0); // set errno to 0 * handle.invoke(capturedState); * int errno = (int) errnoHandle.get(capturedState, 0L); * // use errno diff --git a/src/java.base/share/classes/java/net/URLStreamHandler.java b/src/java.base/share/classes/java/net/URLStreamHandler.java index f66902a451e..76807d27cee 100644 --- a/src/java.base/share/classes/java/net/URLStreamHandler.java +++ b/src/java.base/share/classes/java/net/URLStreamHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2026, 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 @@ -481,14 +481,33 @@ public abstract class URLStreamHandler { * @return a string representation of the {@code URL} argument. */ protected String toExternalForm(URL u) { - String s; + // The fast paths and branch-free concatenations in this method are here for + // a reason and should not be updated without checking performance figures. + + // Optionality, subtly different for authority + boolean emptyAuth = u.getAuthority() == null || u.getAuthority().isEmpty(); + boolean emptyPath = u.getPath() == null; + boolean emptyQuery = u.getQuery() == null; + boolean emptyRef = u.getRef() == null; + var path = emptyPath ? "" : u.getPath(); + // Fast paths for empty components + if (emptyQuery && emptyRef) { + return emptyAuth + ? (u.getProtocol() + ":" + path) + : (u.getProtocol() + "://" + u.getAuthority() + path); + } + // Prefer locals for efficient concatenation + var authSep = emptyAuth ? ":" : "://"; + var auth = emptyAuth ? "" : u.getAuthority(); + var querySep = emptyQuery ? "" : "?"; + var query = emptyQuery ? "" : u.getQuery(); + var refSep = emptyRef ? "" : "#"; + var ref = emptyRef ? "" : u.getRef(); return u.getProtocol() - + ':' - + ((s = u.getAuthority()) != null && !s.isEmpty() - ? "//" + s : "") - + ((s = u.getPath()) != null ? s : "") - + ((s = u.getQuery()) != null ? '?' + s : "") - + ((s = u.getRef()) != null ? '#' + s : ""); + + authSep + auth + + path + + querySep + query + + refSep + ref; } /** diff --git a/src/java.base/share/classes/java/util/spi/LocaleServiceProvider.java b/src/java.base/share/classes/java/util/spi/LocaleServiceProvider.java index 5e7b61e6c57..abb9f3aca38 100644 --- a/src/java.base/share/classes/java/util/spi/LocaleServiceProvider.java +++ b/src/java.base/share/classes/java/util/spi/LocaleServiceProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -166,7 +166,7 @@ import java.util.Locale; * Common Locale Data Repository (CLDR) * to implement locale-sensitive APIs in the {@code java.util} and * {@code java.text} packages. This locale data derives the set of locales - * supported by the Java runtime environment. The following table lists the + * supported by the Java runtime environment. The following tables list the * version of CLDR used in each JDK release. Unless otherwise specified, all * update releases in a given JDK release family use the same CLDR version. * Note that the CLDR locale data are subject to change. Users should not assume @@ -175,6 +175,9 @@ import java.util.Locale; * Refer to CLDR Releases * for the deltas between their releases. * + * * * * @@ -185,22 +188,38 @@ import java.util.Locale; * * * + * + * + * + * + * + * + * + * + * + *
JDK releases and supported CLDR versions
JDK releaseCLDR 48
JDK 25CLDR 47
JDK 21CLDR 43
JDK 17CLDR 39
JDK 11CLDR 33
JDK 8CLDR 21.0.1
+ *

+ * Show other JDK releases + * + * + * + * + * + * + * * * * * * * - * - * * * * * * * - * - * * * * @@ -211,16 +230,13 @@ import java.util.Locale; * * * - * - * * * * * - * - * * *
Other JDK releases and supported CLDR + * versions
JDK releaseCLDR version
JDK 24CLDR 46
JDK 23CLDR 45
JDK 22CLDR 44
JDK 21CLDR 43
JDK 20CLDR 42
JDK 19CLDR 41
JDK 18CLDR 39
JDK 17CLDR 39
JDK 16CLDR 38
JDK 15CLDR 35.1
JDK 12CLDR 33
JDK 11CLDR 33
JDK 10CLDR 29
JDK 9CLDR 29
JDK 8CLDR 21.0.1
+ *
* * @since 1.6 */ diff --git a/src/java.base/share/classes/java/util/zip/InflaterOutputStream.java b/src/java.base/share/classes/java/util/zip/InflaterOutputStream.java index 31c51509a76..abe4e069915 100644 --- a/src/java.base/share/classes/java/util/zip/InflaterOutputStream.java +++ b/src/java.base/share/classes/java/util/zip/InflaterOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2026, 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 @@ -31,14 +31,14 @@ import java.io.OutputStream; import java.util.Objects; /** - * Implements an output stream filter for uncompressing data stored in the + * Implements an output stream filter for decompressing data stored in the * "deflate" compression format. * *

Decompressor Usage

* An {@code InflaterOutputStream} created without * specifying a {@linkplain Inflater decompressor} will create a decompressor * at construction time, and close the decompressor when the output stream - * is {@linkplain #close closed}. + * is {@linkplain #close closed} or when {@link #finish()} is called. *

* If a decompressor is specified when creating a {@code InflaterOutputStream}, it is the * responsibility of the caller to {@linkplain Inflater#close close} the @@ -49,7 +49,6 @@ import java.util.Objects; * stream, either directly, or with the {@code try}-with-resources statement. * * @since 1.6 - * @author David R Tribble (david@tribble.com) * * @see InflaterInputStream * @see DeflaterInputStream @@ -60,7 +59,7 @@ public class InflaterOutputStream extends FilterOutputStream { /** Decompressor for this stream. */ protected final Inflater inf; - /** Output buffer for writing uncompressed data. */ + /** Output buffer for writing decompressed data. */ protected final byte[] buf; /** Temporary write buffer. */ @@ -72,6 +71,10 @@ public class InflaterOutputStream extends FilterOutputStream { /** true iff {@link #close()} has been called. */ private boolean closed = false; + // set to true if finish() was called and this InflaterOutputStream + // had created its own Inflater at construction time. + private boolean defaultInflaterClosed; + /** * Checks to make sure that this stream has not been closed. */ @@ -88,7 +91,7 @@ public class InflaterOutputStream extends FilterOutputStream { * The decompressor will be closed when this output stream * is {@linkplain #close() closed}. * - * @param out output stream to write the uncompressed data to + * @param out output stream to write the decompressed data to * @throws NullPointerException if {@code out} is null */ public InflaterOutputStream(OutputStream out) { @@ -104,7 +107,7 @@ public class InflaterOutputStream extends FilterOutputStream { * {@linkplain ##decompressor-usage will not close} the given * {@linkplain Inflater decompressor}. * - * @param out output stream to write the uncompressed data to + * @param out output stream to write the decompressed data to * @param infl decompressor ("inflater") for this stream * @throws NullPointerException if {@code out} or {@code infl} is null */ @@ -120,7 +123,7 @@ public class InflaterOutputStream extends FilterOutputStream { * {@linkplain ##decompressor-usage will not close} the given * {@linkplain Inflater decompressor}. * - * @param out output stream to write the uncompressed data to + * @param out output stream to write the decompressed data to * @param infl decompressor ("inflater") for this stream * @param bufLen decompression buffer size * @throws IllegalArgumentException if {@code bufLen <= 0} @@ -143,27 +146,45 @@ public class InflaterOutputStream extends FilterOutputStream { } /** - * Writes any remaining uncompressed data to the output stream and closes + * Writes any remaining decompressed data to the output stream and closes * the underlying output stream. * + * @implSpec If not already closed, this method calls {@link #finish()} before + * closing the underlying output stream. + * * @throws IOException if an I/O error occurs */ @Override public void close() throws IOException { - if (!closed) { - // Complete the uncompressed output + if (closed) { + return; + } + IOException toThrow = null; + // Complete the decompressed output + try { + finish(); + } catch (IOException ioe) { + toThrow = ioe; + } finally { try { - finish(); - } finally { out.close(); - closed = true; + } catch (IOException ioe) { + if (toThrow == null) { + toThrow = ioe; + } else if (toThrow != ioe) { + toThrow.addSuppressed(ioe); + } } + closed = true; + } + if (toThrow != null) { + throw toThrow; } } /** - * Flushes this output stream, forcing any pending buffered output bytes to be - * written. + * Flushes this output stream, writing any pending buffered decompressed data to + * the underlying output stream. * * @throws IOException if an I/O error occurs or this stream is already * closed @@ -184,7 +205,7 @@ public class InflaterOutputStream extends FilterOutputStream { break; } - // Write the uncompressed output data block + // Write the decompressed output data block out.write(buf, 0, n); } super.flush(); @@ -200,12 +221,18 @@ public class InflaterOutputStream extends FilterOutputStream { } /** - * Finishes writing uncompressed data to the output stream without closing - * the underlying stream. Use this method when applying multiple filters in - * succession to the same output stream. + * Writes any pending buffered decompressed data to the underlying output stream, + * without closing the underlying stream. * - * @throws IOException if an I/O error occurs or this stream is already - * closed + * @implSpec This method calls {@link #flush()} to write any pending buffered + * decompressed data. + *

+ * If this {@code InflaterOutputStream} was created without specifying + * a {@linkplain Inflater decompressor}, then this method closes the decompressor + * that was created at construction time. The {@code InflaterOutputStream} cannot + * then be used for any further writes. + * + * @throws IOException if an I/O error occurs or this stream is already closed */ public void finish() throws IOException { ensureOpen(); @@ -214,11 +241,12 @@ public class InflaterOutputStream extends FilterOutputStream { flush(); if (usesDefaultInflater) { inf.end(); + this.defaultInflaterClosed = true; } } /** - * Writes a byte to the uncompressed output stream. + * Writes a byte to the decompressed output stream. * * @param b a single byte of compressed data to decompress and write to * the output stream @@ -234,7 +262,7 @@ public class InflaterOutputStream extends FilterOutputStream { } /** - * Writes an array of bytes to the uncompressed output stream. + * Writes an array of bytes to the decompressed output stream. * * @param b buffer containing compressed data to decompress and write to * the output stream @@ -251,15 +279,22 @@ public class InflaterOutputStream extends FilterOutputStream { public void write(byte[] b, int off, int len) throws IOException { // Sanity checks ensureOpen(); + // check if this InflaterOutputStream had constructed its own + // Inflater at construction time and has been + // rendered unusable for writes due to finish() being called + // on it. + if (usesDefaultInflater && defaultInflaterClosed) { + throw new IOException("Inflater closed"); + } if (b == null) { - throw new NullPointerException("Null buffer for read"); + throw new NullPointerException("Null input buffer"); } Objects.checkFromIndexSize(off, len, b.length); if (len == 0) { return; } - // Write uncompressed data to the output stream + // Write decompressed data to the output stream try { for (;;) { int n; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java index 07406b2ee7f..adc595813ee 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -1047,13 +1047,11 @@ public final class VerifierImpl { no_control_flow = false; break; case IF_ACMPEQ : case IF_ACMPNE : - current_frame.pop_stack( - VerificationType.reference_check); + current_frame.pop_stack(object_type()); // fall through case IFNULL : case IFNONNULL : - current_frame.pop_stack( - VerificationType.reference_check); + current_frame.pop_stack(object_type()); target = bcs.dest(); stackmap_table.check_jump_target (current_frame, target); diff --git a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java index 5942cefa2a1..a698440c15d 100644 --- a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java +++ b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -64,25 +64,6 @@ public @interface PreviewFeature { * Values should be annotated with the feature's {@code JEP}. */ public enum Feature { - // The JDK build process involves creating an interim javac which is then - // used to compile the rest of the JDK. The jdk.internal.javac.PreviewFeature - // annotation from the current sources is used when compiling interim javac. - // That's because the javac APIs of the current sources may be annotated with - // this annotation and they may be using the enum constants of the current sources. - // Furthermore, when compiling interim javac, the class files from the bootstrap JDK get - // used and those may also contain the PreviewFeature annotation. However, they may be - // using the enum constants of the bootstrap JDK's PreviewFeature annotation. - // If javac sees an annotation with an unknown enum constant, it produces a warning, - // and that in turn fails the build. - // So, in the current sources, we need to preserve the PreviewFeature enum constants - // for as long as the interim javac build needs it. As a result, we retain PreviewFeature - // enum constants for preview features that are present in the bootstrap JDK. - // Older constants can be removed. - // - // For example, Class-File API became final in JDK 24. As soon as JDK 23 was dropped as - // the bootstrap JDK, the CLASSFILE_API enum constant became eligible for removal. - - //--- @JEP(number=525, title="Structured Concurrency", status="Sixth Preview") STRUCTURED_CONCURRENCY, @JEP(number = 526, title = "Lazy Constants", status = "Second Preview") diff --git a/src/java.base/share/classes/sun/security/ec/XDHPublicKeyImpl.java b/src/java.base/share/classes/sun/security/ec/XDHPublicKeyImpl.java index e6f8961f412..e161880f883 100644 --- a/src/java.base/share/classes/sun/security/ec/XDHPublicKeyImpl.java +++ b/src/java.base/share/classes/sun/security/ec/XDHPublicKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -54,7 +54,11 @@ public final class XDHPublicKeyImpl extends X509Key implements XECPublicKey { this.paramSpec = new NamedParameterSpec(params.getName()); this.algid = new AlgorithmId(params.getOid()); - this.u = u.mod(params.getP()); + + // RFC 7748 Section 5 requires the MSB of `u` to be zeroed for X25519 + this.u = (params == XECParameters.X448) ? + u.mod(params.getP()) : + u.clearBit(255).mod(params.getP()); byte[] u_arr = this.u.toByteArray(); reverse(u_arr); @@ -72,6 +76,7 @@ public final class XDHPublicKeyImpl extends X509Key implements XECPublicKey { XECParameters params = XECParameters.get(InvalidKeyException::new, algid); this.paramSpec = new NamedParameterSpec(params.getName()); + // construct the BigInteger representation byte[] u_arr = getKey().toByteArray(); reverse(u_arr); diff --git a/src/java.base/share/classes/sun/security/ec/XECOperations.java b/src/java.base/share/classes/sun/security/ec/XECOperations.java index dd8aa482cc3..0ea39a10d40 100644 --- a/src/java.base/share/classes/sun/security/ec/XECOperations.java +++ b/src/java.base/share/classes/sun/security/ec/XECOperations.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -89,7 +89,11 @@ public class XECOperations { */ public byte[] encodedPointMultiply(byte[] k, BigInteger u) { pruneK(k); - ImmutableIntegerModuloP elemU = field.getElement(u); + + ImmutableIntegerModuloP elemU = (params == XECParameters.X448) ? + field.getElement(u) : + field.getElement(u.clearBit(255)); + return pointMultiply(k, elemU).asByteArray(params.getBytes()); } diff --git a/src/java.base/share/classes/sun/security/ssl/Hybrid.java b/src/java.base/share/classes/sun/security/ssl/Hybrid.java index e3e2cfa0b23..43634ce2f34 100644 --- a/src/java.base/share/classes/sun/security/ssl/Hybrid.java +++ b/src/java.base/share/classes/sun/security/ssl/Hybrid.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -355,7 +355,7 @@ public class Hybrid { int to, String algorithm) throws DecapsulateException { int expectedEncSize = engineEncapsulationSize(); if (encapsulation.length != expectedEncSize) { - throw new IllegalArgumentException( + throw new DecapsulateException( "Invalid key encapsulation message length: " + encapsulation.length + ", expected = " + expectedEncSize); diff --git a/src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomial25519.java b/src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomial25519.java new file mode 100644 index 00000000000..c8f23da417e --- /dev/null +++ b/src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomial25519.java @@ -0,0 +1,531 @@ +/* + * Copyright (c) 2026, 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. + */ + +package sun.security.util.math.intpoly; + +import java.math.BigInteger; + +public final class IntegerPolynomial25519 extends IntegerPolynomial { + private static final int BITS_PER_LIMB = 51; + private static final int NUM_LIMBS = 5; + private static final int MAX_ADDS = 1; + public static final BigInteger MODULUS = evaluateModulus(); + private static final long CARRY_ADD = 1L << (BITS_PER_LIMB - 1); + private static final long LIMB_MASK = -1L >>> (64 - BITS_PER_LIMB); + + public static final IntegerPolynomial25519 ONE = + new IntegerPolynomial25519(); + + private IntegerPolynomial25519() { + super(BITS_PER_LIMB, NUM_LIMBS, MAX_ADDS, MODULUS); + } + + private static BigInteger evaluateModulus() { + BigInteger result = BigInteger.valueOf(2).pow(255); + result = result.subtract(BigInteger.valueOf(19)); + + return result; + } + + /** + * Carry from a range of limb positions. + * Override for performance (unnesting). + * + * @param limbs [in|out] the limbs for carry operation. + * @param start [in] the starting position of carry. + * @param end [in] the ending position of carry. + */ + @Override + protected void carry(long[] limbs, int start, int end) { + long carry; + + for (int i = start; i < end; i++) { + carry = (limbs[i] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[i] -= (carry << BITS_PER_LIMB); + limbs[i + 1] += carry; + } + } + + /** + * Carry operation for all limb positions. + * Override for performance (unroll and unnesting). + * + * @param limbs [in|out] the limbs for carry operation. + */ + @Override + protected void carry(long[] limbs) { + long carry = (limbs[0] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[0] -= carry << BITS_PER_LIMB; + limbs[1] += carry; + + carry = (limbs[1] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[1] -= carry << BITS_PER_LIMB; + limbs[2] += carry; + + carry = (limbs[2] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[2] -= carry << BITS_PER_LIMB; + limbs[3] += carry; + + carry = (limbs[3] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[3] -= carry << BITS_PER_LIMB; + limbs[4] += carry; + } + + /** + * Multiply limbs by scalar value. + * Superclass assumes that limb primitive radix > (bits per limb * 2) + * + * @param a [in|out] the limbs to multiply a carry operation. 'a' is + * assumed to be reduced. + * @param b [in] the scalar value to be muliplied with the limbs. + */ + @Override + protected void multByInt(long[] a, long b) { + long aa0 = a[0]; + long aa1 = a[1]; + long aa2 = a[2]; + long aa3 = a[3]; + long aa4 = a[4]; + + long bb0 = b; + + final long shift1 = 64 - BITS_PER_LIMB; + final long shift2 = BITS_PER_LIMB; + + long d0; // low digit from multiplication + long dd0; // high digit from multiplication + // multiplication result digits for each column + long c0, c1, c2, c3, c4, c5; + + // Row 0 - multiply by aa0 + d0 = aa0 * bb0; + dd0 = Math.multiplyHigh(aa0, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + c0 = d0; + c1 = dd0; + + // Row 1 - multiply by aa1 + d0 = aa1 * bb0; + dd0 = Math.multiplyHigh(aa1, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + c1 += d0; + c2 = dd0; + + // Row 2 - multiply by aa2 + d0 = aa2 * bb0; + dd0 = Math.multiplyHigh(aa2, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + c2 += d0; + c3 = dd0; + + // Row 3 - multiply by aa3 + d0 = aa3 * bb0; + dd0 = Math.multiplyHigh(aa3, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + c3 += d0; + c4 = dd0; + + // Row 4 - multiply by aa4 + d0 = aa4 * bb0; + dd0 = Math.multiplyHigh(aa4, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + c4 += d0; + c5 = dd0; + + // Perform pseudo-Mersenne reduction + a[0] = c0 + (19 * c5); + + a[1] = c1; + a[2] = c2; + a[3] = c3; + a[4] = c4; + + reduce(a); + } + + /** + * Carry in all positions and reduce high order limb. + * + * @param limbs [in|out] the limbs to carry and reduce. + */ + protected void reduce(long[] limbs) { + long carry = (limbs[3] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[3] -= carry << BITS_PER_LIMB; + limbs[4] += carry; + + carry = (limbs[4] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[4] -= carry << BITS_PER_LIMB; + + limbs[0] += 19 * carry; + + carry = (limbs[0] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[0] -= carry << BITS_PER_LIMB; + limbs[1] += carry; + + carry = (limbs[1] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[1] -= carry << BITS_PER_LIMB; + limbs[2] += carry; + + carry = (limbs[2] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[2] -= carry << BITS_PER_LIMB; + limbs[3] += carry; + + carry = (limbs[3] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[3] -= carry << BITS_PER_LIMB; + limbs[4] += carry; + } + + /** + * Reduces digit 'v' at limb position 'i' to a lower limb. + * + * @param limbs [in|out] the limbs to reduce in. + * @param v [in] the digit to reduce to the lower limb. + * @param i [in] the limbs to reduce from. + */ + protected void reduceIn(long[] limbs, long v, int i) { + limbs[i - NUM_LIMBS] += 19 * v; + } + + /** + * Carry from high order limb and reduce to the lower order limb. Assumed + * to be called two times to propagate the carries. + * + * @param limbs [in|out] the limbs to fully carry and reduce. + */ + protected void finalCarryReduceLast(long[] limbs) { + long carry = limbs[4] >> BITS_PER_LIMB; + + limbs[4] -= carry << BITS_PER_LIMB; + limbs[0] += 19 * carry; + } + + /** + * Multiply two limbs using a high/low digit technique that allows for + * larger limb sizes. It is assumed that both limbs have already been + * reduced. + * + * @param a [in] the limb operand to multiply. + * @param b [in] the limb operand to multiply. + * @param r [out] the product of the limbs operands that is fully reduced. + */ + protected void mult(long[] a, long[] b, long[] r) { + long aa0 = a[0]; + long aa1 = a[1]; + long aa2 = a[2]; + long aa3 = a[3]; + long aa4 = a[4]; + + long bb0 = b[0]; + long bb1 = b[1]; + long bb2 = b[2]; + long bb3 = b[3]; + long bb4 = b[4]; + + final long shift1 = 64 - BITS_PER_LIMB; + final long shift2 = BITS_PER_LIMB; + + long d0, d1, d2, d3, d4; // low digits from multiplication + long dd0, dd1, dd2, dd3, dd4; // high digits from multiplication + // multiplication result digits for each column + long c0, c1, c2, c3, c4, c5, c6, c7, c8, c9; + + // Row 0 - multiply by aa0 + d0 = aa0 * bb0; + dd0 = Math.multiplyHigh(aa0, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa0 * bb1; + dd1 = Math.multiplyHigh(aa0, bb1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa0 * bb2; + dd2 = Math.multiplyHigh(aa0, bb2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa0 * bb3; + dd3 = Math.multiplyHigh(aa0, bb3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa0 * bb4; + dd4 = Math.multiplyHigh(aa0, bb4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c0 = d0; + c1 = d1 + dd0; + c2 = d2 + dd1; + c3 = d3 + dd2; + c4 = d4 + dd3; + c5 = dd4; + + // Row 1 - multiply by aa1 + d0 = aa1 * bb0; + dd0 = Math.multiplyHigh(aa1, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa1 * bb1; + dd1 = Math.multiplyHigh(aa1, bb1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa1 * bb2; + dd2 = Math.multiplyHigh(aa1, bb2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa1 * bb3; + dd3 = Math.multiplyHigh(aa1, bb3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa1 * bb4; + dd4 = Math.multiplyHigh(aa1, bb4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c1 += d0; + c2 += d1 + dd0; + c3 += d2 + dd1; + c4 += d3 + dd2; + c5 += d4 + dd3; + c6 = dd4; + + // Row 2 - multiply by aa2 + d0 = aa2 * bb0; + dd0 = Math.multiplyHigh(aa2, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa2 * bb1; + dd1 = Math.multiplyHigh(aa2, bb1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa2 * bb2; + dd2 = Math.multiplyHigh(aa2, bb2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa2 * bb3; + dd3 = Math.multiplyHigh(aa2, bb3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa2 * bb4; + dd4 = Math.multiplyHigh(aa2, bb4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c2 += d0; + c3 += d1 + dd0; + c4 += d2 + dd1; + c5 += d3 + dd2; + c6 += d4 + dd3; + c7 = dd4; + + // Row 3 - multiply by aa3 + d0 = aa3 * bb0; + dd0 = Math.multiplyHigh(aa3, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa3 * bb1; + dd1 = Math.multiplyHigh(aa3, bb1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa3 * bb2; + dd2 = Math.multiplyHigh(aa3, bb2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa3 * bb3; + dd3 = Math.multiplyHigh(aa3, bb3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa3 * bb4; + dd4 = Math.multiplyHigh(aa3, bb4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c3 += d0; + c4 += d1 + dd0; + c5 += d2 + dd1; + c6 += d3 + dd2; + c7 += d4 + dd3; + c8 = dd4; + + // Row 4 - multiply by aa4 + d0 = aa4 * bb0; + dd0 = Math.multiplyHigh(aa4, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa4 * bb1; + dd1 = Math.multiplyHigh(aa4, bb1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa4 * bb2; + dd2 = Math.multiplyHigh(aa4, bb2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa4 * bb3; + dd3 = Math.multiplyHigh(aa4, bb3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa4 * bb4; + dd4 = Math.multiplyHigh(aa4, bb4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c4 += d0; + c5 += d1 + dd0; + c6 += d2 + dd1; + c7 += d3 + dd2; + c8 += d4 + dd3; + c9 = dd4; + + // Perform pseudo-Mersenne reduction + r[0] = c0 + (19 * c5); + r[1] = c1 + (19 * c6); + r[2] = c2 + (19 * c7); + r[3] = c3 + (19 * c8); + r[4] = c4 + (19 * c9); + + reduce(r); + } + + /** + * Takes a single limb and squares it using a high/low digit technique that + * allows for larger limb sizes. It is assumed that the limb input has + * already been reduced. + * + * @param a [in] the limb operand to square. + * @param r [out] the resulting square of the limb which is fully reduced. + */ + protected void square(long[] a, long[] r) { + long aa0 = a[0]; + long aa1 = a[1]; + long aa2 = a[2]; + long aa3 = a[3]; + long aa4 = a[4]; + + final long shift1 = 64 - BITS_PER_LIMB; + final long shift2 = BITS_PER_LIMB; + + long d0, d1, d2, d3, d4; // low digits from multiplication + long dd0, dd1, dd2, dd3, dd4; // high digits from multiplication + // multiplication result digits for each column + long c0, c1, c2, c3, c4, c5, c6, c7, c8, c9; + + // Row 0 - multiply by aa0 + d0 = aa0 * aa0; + dd0 = Math.multiplyHigh(aa0, aa0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa0 * aa1; + dd1 = Math.multiplyHigh(aa0, aa1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa0 * aa2; + dd2 = Math.multiplyHigh(aa0, aa2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa0 * aa3; + dd3 = Math.multiplyHigh(aa0, aa3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa0 * aa4; + dd4 = Math.multiplyHigh(aa0, aa4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c0 = d0; + c1 = (d1 << 1) + dd0; + c2 = (d2 + dd1) << 1; + c3 = (d3 + dd2) << 1; + c4 = (d4 + dd3) << 1; + c5 = dd4 << 1; + + // Row 1 - multiply by aa1 + d1 = aa1 * aa1; + dd1 = Math.multiplyHigh(aa1, aa1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa1 * aa2; + dd2 = Math.multiplyHigh(aa1, aa2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa1 * aa3; + dd3 = Math.multiplyHigh(aa1, aa3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa1 * aa4; + dd4 = Math.multiplyHigh(aa1, aa4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c2 += d1; + c3 += (d2 << 1) + dd1; + c4 += (d3 + dd2) << 1; + c5 += (d4 + dd3) << 1; + c6 = dd4 << 1; + + // Row 2 - multiply by aa2 + d2 = aa2 * aa2; + dd2 = Math.multiplyHigh(aa2, aa2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa2 * aa3; + dd3 = Math.multiplyHigh(aa2, aa3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa2 * aa4; + dd4 = Math.multiplyHigh(aa2, aa4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c4 += d2; + c5 += (d3 << 1) + dd2; + c6 += (d4 + dd3) << 1; + c7 = dd4 << 1; + + // Row 3 - multiply by aa3 + d3 = aa3 * aa3; + dd3 = Math.multiplyHigh(aa3, aa3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa3 * aa4; + dd4 = Math.multiplyHigh(aa3, aa4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c6 += d3; + c7 += (d4 << 1) + dd3; + c8 = dd4 << 1; + + // Row 4 - multiply by aa4 + d4 = aa4 * aa4; + dd4 = Math.multiplyHigh(aa4, aa4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c8 += d4; + c9 = dd4; + + // Perform pseudo-Mersenne reduction + r[0] = c0 + (19 * c5); + r[1] = c1 + (19 * c6); + r[2] = c2 + (19 * c7); + r[3] = c3 + (19 * c8); + r[4] = c4 + (19 * c9); + + reduce(r); + } +} diff --git a/src/java.base/share/man/java.md b/src/java.base/share/man/java.md index 8cfccff5abe..18d64b3a4c2 100644 --- a/src/java.base/share/man/java.md +++ b/src/java.base/share/man/java.md @@ -2944,26 +2944,6 @@ they're used. the configuration of the computer (RAM and CPU). By default, the option is disabled and the heap sizes are configured less aggressively. -[`-XX:+NeverActAsServerClassMachine`]{#-XX__NeverActAsServerClassMachine} -: Enable the "Client VM emulation" mode which only uses the C1 JIT compiler, - a 32Mb CodeCache and the Serial GC. The maximum amount of memory that the - JVM may use is set to 1GB by default. The string "emulated-client" is added - to the JVM version string. - - By default the flag is set to `true` only on Windows in 32-bit mode and - `false` in all other cases. - - The "Client VM emulation" mode will not be enabled if any of the following - flags are used on the command line: - - ``` - -XX:{+|-}TieredCompilation - -XX:CompilationMode=mode - -XX:TieredStopAtLevel=n - -XX:{+|-}EnableJVMCI - -XX:{+|-}UseJVMCICompiler - ``` - ## Obsolete Java Options These `java` options are still accepted but ignored, and a warning is issued @@ -2976,6 +2956,26 @@ when they're used. 396](https://openjdk.org/jeps/396) and made obsolete in JDK 17 by [JEP 403](https://openjdk.org/jeps/403). +[`-XX:+NeverActAsServerClassMachine`]{#-XX__NeverActAsServerClassMachine} +: Enabled the "Client VM emulation" mode, which used only the C1 JIT compiler, + a 32Mb CodeCache, and the Serial GC. The maximum amount of memory that the + JVM could use was set to 1GB by default. The string "emulated-client" was added + to the JVM version string. + + By default the flag was set to `true` only on Windows in 32-bit mode and + `false` in all other cases. + + The "Client VM emulation" mode was not enabled if any of the following + flags were used on the command line: + + ``` + -XX:{+|-}TieredCompilation + -XX:CompilationMode=mode + -XX:TieredStopAtLevel=n + -XX:{+|-}EnableJVMCI + -XX:{+|-}UseJVMCICompiler + ``` + ## Removed Java Options No documented java options have been removed in JDK @@VERSION_SPECIFICATION@@. diff --git a/src/java.base/share/native/libfallbackLinker/fallbackLinker.c b/src/java.base/share/native/libfallbackLinker/fallbackLinker.c index 3f57a2b97cf..7519efd93bb 100644 --- a/src/java.base/share/native/libfallbackLinker/fallbackLinker.c +++ b/src/java.base/share/native/libfallbackLinker/fallbackLinker.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -87,14 +87,32 @@ Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1get_1struct_1offsets(JNI return ffi_get_struct_offsets((ffi_abi) abi, jlong_to_ptr(type), jlong_to_ptr(offsets)); } -static void do_capture_state(int32_t* value_ptr, int captured_state_mask) { - // keep in synch with jdk.internal.foreign.abi.CapturableState - enum PreservableValues { - NONE = 0, - GET_LAST_ERROR = 1, - WSA_GET_LAST_ERROR = 1 << 1, - ERRNO = 1 << 2 - }; +// keep in synch with jdk.internal.foreign.abi.CapturableState +enum PreservableValues { + NONE = 0, + GET_LAST_ERROR = 1, + WSA_GET_LAST_ERROR = 1 << 1, + ERRNO = 1 << 2 +}; + +static void do_capture_state_pre(int32_t* value_ptr, int captured_state_mask) { +#ifdef _WIN64 + if (captured_state_mask & GET_LAST_ERROR) { + SetLastError(*value_ptr); + } + value_ptr++; + if (captured_state_mask & WSA_GET_LAST_ERROR) { + WSASetLastError(*value_ptr); + *value_ptr = WSAGetLastError(); + } + value_ptr++; +#endif + if (captured_state_mask & ERRNO) { + errno = *value_ptr; + } +} + +static void do_capture_state_post(int32_t* value_ptr, int captured_state_mask) { #ifdef _WIN64 if (captured_state_mask & GET_LAST_ERROR) { *value_ptr = GetLastError(); @@ -142,10 +160,15 @@ Java_jdk_internal_foreign_abi_fallback_LibFallback_doDowncall(JNIEnv* env, jclas } } + if (captured_state_mask != 0) { + // Copy the contents of the capture state buffer into thread local + do_capture_state_pre(captured_state_addr, captured_state_mask); + } + ffi_call(jlong_to_ptr(cif), jlong_to_ptr(fn), jlong_to_ptr(rvalue), jlong_to_ptr(avalues)); if (captured_state_mask != 0) { - do_capture_state(captured_state_addr, captured_state_mask); + do_capture_state_post(captured_state_addr, captured_state_mask); } if (heapBases != NULL) { diff --git a/src/java.base/share/native/libverify/check_code.c b/src/java.base/share/native/libverify/check_code.c index 5a8f50cd0a0..4830fedb97b 100644 --- a/src/java.base/share/native/libverify/check_code.c +++ b/src/java.base/share/native/libverify/check_code.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2026, 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 @@ -2162,8 +2162,7 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac break; if ( (GET_ITEM_TYPE(top_type) == ITEM_NewObject || (GET_ITEM_TYPE(top_type) == ITEM_InitObject)) - && ((opcode == JVM_OPC_astore) || (opcode == JVM_OPC_aload) - || (opcode == JVM_OPC_ifnull) || (opcode == JVM_OPC_ifnonnull))) + && ((opcode == JVM_OPC_astore) || (opcode == JVM_OPC_aload))) break; /* The 2nd edition VM of the specification allows field * initializations before the superclass initializer, diff --git a/src/java.base/unix/native/jspawnhelper/jspawnhelper.c b/src/java.base/unix/native/jspawnhelper/jspawnhelper.c index d2302d0c2e7..2523fb30acc 100644 --- a/src/java.base/unix/native/jspawnhelper/jspawnhelper.c +++ b/src/java.base/unix/native/jspawnhelper/jspawnhelper.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, 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 @@ -26,14 +26,16 @@ #include #include #include +#include +#include #include #include #include #include #include -#include #include "childproc.h" +#include "childproc_errorcodes.h" extern int errno; @@ -41,36 +43,34 @@ extern int errno; void *mptr; \ mptr = malloc (Y); \ if (mptr == 0) { \ - error (fdout, ERR_MALLOC); \ + sendErrorCodeAndExit (fdout, ESTEP_JSPAWN_ALLOC_FAILED, (int)Y, errno); \ } \ X = mptr; \ } -#define ERR_MALLOC 1 -#define ERR_PIPE 2 -#define ERR_ARGS 3 - #ifndef VERSION_STRING #error VERSION_STRING must be defined #endif -void error (int fd, int err) { - if (write (fd, &err, sizeof(err)) != sizeof(err)) { - /* Not sure what to do here. I have no one to speak to. */ - exit(0x80 + err); +/* Attempts to send an error code to the parent (which may or may not + * work depending on whether the fail pipe exists); then exits with an + * error code corresponding to the fail step. */ +void sendErrorCodeAndExit(int failpipe_fd, int step, int hint, int errno_) { + errcode_t errcode; + buildErrorCode(&errcode, step, hint, errno_); + if (failpipe_fd == -1 || !sendErrorCode(failpipe_fd, errcode)) { + /* Write error code to stdout, in the hope someone reads this. */ + printf("jspawnhelper fail: " ERRCODE_FORMAT "\n", ERRCODE_FORMAT_ARGS(errcode)); } - exit (1); + exit(exitCodeFromErrorCode(errcode)); } -void shutItDown() { - fprintf(stdout, "jspawnhelper version %s\n", VERSION_STRING); - fprintf(stdout, "This command is not for general use and should "); - fprintf(stdout, "only be run as the result of a call to\n"); - fprintf(stdout, "ProcessBuilder.start() or Runtime.exec() in a java "); - fprintf(stdout, "application\n"); - fflush(stdout); - _exit(1); -} +static const char* usageErrorText = + "jspawnhelper version " VERSION_STRING "\n" + "This command is not for general use and should " + "only be run as the result of a call to\n" + "ProcessBuilder.start() or Runtime.exec() in a java " + "application\n"; /* * read the following off the pipefd @@ -84,22 +84,31 @@ void initChildStuff (int fdin, int fdout, ChildStuff *c) { int bufsize, offset=0; int magic; int res; + const int step = ESTEP_JSPAWN_RCV_CHILDSTUFF_COMM_FAIL; + int substep = 0; res = readFully (fdin, &magic, sizeof(magic)); - if (res != 4 || magic != magicNumber()) { - error (fdout, ERR_PIPE); + if (res != 4) { + sendErrorCodeAndExit(fdout, step, substep, errno); + } + + substep ++; + if (magic != magicNumber()) { + sendErrorCodeAndExit(fdout, step, substep, errno); } #ifdef DEBUG jtregSimulateCrash(0, 5); #endif + substep ++; if (readFully (fdin, c, sizeof(*c)) != sizeof(*c)) { - error (fdout, ERR_PIPE); + sendErrorCodeAndExit(fdout, step, substep, errno); } + substep ++; if (readFully (fdin, &sp, sizeof(sp)) != sizeof(sp)) { - error (fdout, ERR_PIPE); + sendErrorCodeAndExit(fdout, step, substep, errno); } bufsize = sp.argvBytes + sp.envvBytes + @@ -107,8 +116,9 @@ void initChildStuff (int fdin, int fdout, ChildStuff *c) { ALLOC(buf, bufsize); + substep++; if (readFully (fdin, buf, bufsize) != bufsize) { - error (fdout, ERR_PIPE); + sendErrorCodeAndExit(fdout, step, substep, errno); } /* Initialize argv[] */ @@ -139,50 +149,63 @@ void initChildStuff (int fdin, int fdout, ChildStuff *c) { offset += sp.parentPathvBytes; } +#ifdef DEBUG +static void checkIsValid(int fd) { + if (!fdIsValid(fd)) { + puts(usageErrorText); + sendErrorCodeAndExit(-1, ESTEP_JSPAWN_INVALID_FD, fd, errno); + } +} +static void checkIsPipe(int fd) { + checkIsValid(fd); + if (!fdIsPipe(fd)) { + puts(usageErrorText); + sendErrorCodeAndExit(-1, ESTEP_JSPAWN_NOT_A_PIPE, fd, errno); + } +} +static void checkFileDescriptorSetup() { + checkIsValid(STDIN_FILENO); + checkIsValid(STDOUT_FILENO); + checkIsValid(STDERR_FILENO); + checkIsPipe(FAIL_FILENO); + checkIsPipe(CHILDENV_FILENO); +} +#endif // DEBUG + int main(int argc, char *argv[]) { ChildStuff c; - struct stat buf; - /* argv[1] contains the fd number to read all the child info */ - int r, fdinr, fdinw, fdout; #ifdef DEBUG jtregSimulateCrash(0, 4); #endif - if (argc != 3) { - fprintf(stdout, "Incorrect number of arguments: %d\n", argc); - shutItDown(); + if (argc != 2) { + printf("Incorrect number of arguments: %d\n", argc); + puts(usageErrorText); + sendErrorCodeAndExit(-1, ESTEP_JSPAWN_ARG_ERROR, 0, 0); } if (strcmp(argv[1], VERSION_STRING) != 0) { - fprintf(stdout, "Incorrect Java version: %s\n", argv[1]); - shutItDown(); + printf("Incorrect Java version: %s\n", argv[1]); + puts(usageErrorText); + sendErrorCodeAndExit(-1, ESTEP_JSPAWN_VERSION_ERROR, 0, 0); } - r = sscanf (argv[2], "%d:%d:%d", &fdinr, &fdinw, &fdout); - if (r == 3 && fcntl(fdinr, F_GETFD) != -1 && fcntl(fdinw, F_GETFD) != -1) { - fstat(fdinr, &buf); - if (!S_ISFIFO(buf.st_mode)) { - fprintf(stdout, "Incorrect input pipe\n"); - shutItDown(); - } - } else { - fprintf(stdout, "Incorrect FD array data: %s\n", argv[2]); - shutItDown(); - } +#ifdef DEBUG + /* Check expected file descriptors */ + checkFileDescriptorSetup(); +#endif - // Close the writing end of the pipe we use for reading from the parent. - // We have to do this before we start reading from the parent to avoid - // blocking in the case the parent exits before we finished reading from it. - close(fdinw); // Deliberately ignore errors (see https://lwn.net/Articles/576478/). - initChildStuff (fdinr, fdout, &c); - // Now set the file descriptor for the pipe's writing end to -1 - // for the case that somebody tries to close it again. - assert(c.childenv[1] == fdinw); - c.childenv[1] = -1; - // The file descriptor for reporting errors back to our parent we got on the command - // line should be the same like the one in the ChildStuff struct we've just read. - assert(c.fail[1] == fdout); + initChildStuff(CHILDENV_FILENO, FAIL_FILENO, &c); + +#ifdef DEBUG + /* Not needed in spawn mode */ + assert(c.in[0] == -1 && c.in[1] == -1 && + c.out[0] == -1 && c.out[1] == -1 && + c.err[0] == -1 && c.err[1] == -1 && + c.fail[0] == -1 && c.fail[1] == -1 && + c.fds[0] == -1 && c.fds[1] == -1 && c.fds[2] == -1); +#endif childProcess (&c); return 0; /* NOT REACHED */ diff --git a/src/java.base/unix/native/libjava/ProcessImpl_md.c b/src/java.base/unix/native/libjava/ProcessImpl_md.c index 12597fbb650..f7d91166e76 100644 --- a/src/java.base/unix/native/libjava/ProcessImpl_md.c +++ b/src/java.base/unix/native/libjava/ProcessImpl_md.c @@ -43,10 +43,13 @@ #include #include #include - +#include +#include +#include #include #include "childproc.h" +#include "childproc_errorcodes.h" /* * @@ -527,27 +530,46 @@ forkChild(ChildStuff *c) { return resultPid; } +/* Given two fds, one of which has to be -1, the other one has to be valid, + * return the valid one. */ +static int eitherOneOf(int fd1, int fd2) { + if (fd2 == -1) { + assert(fdIsValid(fd1)); + return fd1; + } + assert(fd1 == -1); + assert(fdIsValid(fd2)); + return fd2; +} + +static int call_posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *file_actions, int filedes, int newfiledes) { +#ifdef __APPLE__ + /* MacOS is not POSIX-compliant: dup2 file actions specifying the same fd as source and destination + * should be handled as no-op according to spec, but they cause EBADF. */ + if (filedes == newfiledes) { + return 0; + } +#endif + return posix_spawn_file_actions_adddup2(file_actions, filedes, newfiledes); +} + static pid_t spawnChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) { pid_t resultPid; - int i, offset, rval, bufsize, magic; - char *buf, buf1[(3 * 11) + 3]; // "%d:%d:%d\0" - char *hlpargs[4]; + int offset, rval, bufsize, magic; + char* buf; + char* hlpargs[3]; SpawnInfo sp; + posix_spawn_file_actions_t file_actions; + int child_stdin, child_stdout, child_stderr, child_childenv, child_fail = -1; - /* need to tell helper which fd is for receiving the childstuff - * and which fd to send response back on - */ - snprintf(buf1, sizeof(buf1), "%d:%d:%d", c->childenv[0], c->childenv[1], c->fail[1]); /* NULL-terminated argv array. * argv[0] contains path to jspawnhelper, to follow conventions. * argv[1] contains the version string as argument to jspawnhelper - * argv[2] contains the fd string as argument to jspawnhelper */ hlpargs[0] = (char*)helperpath; hlpargs[1] = VERSION_STRING; - hlpargs[2] = buf1; - hlpargs[3] = NULL; + hlpargs[2] = NULL; /* Following items are sent down the pipe to the helper * after it is spawned. @@ -570,19 +592,79 @@ spawnChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) bufsize += sp.dirlen; arraysize(parentPathv, &sp.nparentPathv, &sp.parentPathvBytes); bufsize += sp.parentPathvBytes; - /* We need to clear FD_CLOEXEC if set in the fds[]. - * Files are created FD_CLOEXEC in Java. - * Otherwise, they will be closed when the target gets exec'd */ - for (i=0; i<3; i++) { - if (c->fds[i] != -1) { - int flags = fcntl(c->fds[i], F_GETFD); - if (flags & FD_CLOEXEC) { - fcntl(c->fds[i], F_SETFD, flags & (~FD_CLOEXEC)); - } - } + + /* Prepare file descriptors for jspawnhelper and the target binary. */ + + /* 0: copy of either "in" pipe read fd or the stdin redirect fd */ + child_stdin = eitherOneOf(c->fds[0], c->in[0]); + + /* 1: copy of either "out" pipe write fd or the stdout redirect fd */ + child_stdout = eitherOneOf(c->fds[1], c->out[1]); + + /* 2: redirectErrorStream=1: redirected to child's stdout (Order matters!) + * redirectErrorStream=0: copy of either "err" pipe write fd or stderr redirect fd. */ + if (c->redirectErrorStream) { + child_stderr = STDOUT_FILENO; /* Note: this refers to the future stdout in the child process */ + } else { + child_stderr = eitherOneOf(c->fds[2], c->err[1]); } - rval = posix_spawn(&resultPid, helperpath, 0, 0, (char * const *) hlpargs, environ); + /* 3: copy of the "fail" pipe write fd */ + child_fail = c->fail[1]; + + /* 4: copy of the "childenv" pipe read end */ + child_childenv = c->childenv[0]; + + assert(fdIsValid(child_stdin)); + assert(fdIsValid(child_stdout)); + assert(fdIsValid(child_stderr)); + assert(fdIsPipe(child_fail)); + assert(fdIsPipe(child_childenv)); + /* This must always hold true, unless someone deliberately closed 0, 1, or 2 in the parent JVM. */ + assert(child_fail > STDERR_FILENO); + assert(child_childenv > STDERR_FILENO); + + /* Slot in dup2 file actions. */ + posix_spawn_file_actions_init(&file_actions); + +#ifdef __APPLE__ + /* On MacOS, posix_spawn does not behave in a POSIX-conform way in that the + * kernel closes CLOEXEC file descriptors too early for dup2 file actions to + * copy them after the fork. We have to explicitly prevent that by calling a + * propietary API. */ + posix_spawn_file_actions_addinherit_np(&file_actions, child_stdin); + posix_spawn_file_actions_addinherit_np(&file_actions, child_stdout); + posix_spawn_file_actions_addinherit_np(&file_actions, child_stderr); + posix_spawn_file_actions_addinherit_np(&file_actions, child_fail); + posix_spawn_file_actions_addinherit_np(&file_actions, child_childenv); +#endif + + /* First dup2 stdin/out/err to 0,1,2. After this, we can safely dup2 over the + * original stdin/out/err. */ + if (call_posix_spawn_file_actions_adddup2(&file_actions, child_stdin, STDIN_FILENO) != 0 || + call_posix_spawn_file_actions_adddup2(&file_actions, child_stdout, STDOUT_FILENO) != 0 || + /* Order matters: stderr may be redirected to stdout, so this dup2 must happen after the stdout one. */ + call_posix_spawn_file_actions_adddup2(&file_actions, child_stderr, STDERR_FILENO) != 0) + { + return -1; + } + + /* We dup2 with one intermediary step to prevent accidentally dup2'ing over child_childenv. */ + const int tmp_child_childenv = child_fail < 10 ? 10 : child_fail - 1; + if (call_posix_spawn_file_actions_adddup2(&file_actions, child_childenv, tmp_child_childenv) != 0 || + call_posix_spawn_file_actions_adddup2(&file_actions, child_fail, FAIL_FILENO) != 0 || + call_posix_spawn_file_actions_adddup2(&file_actions, tmp_child_childenv, CHILDENV_FILENO) != 0) + { + return -1; + } + + /* Since we won't use these in jspawnhelper, reset them all */ + c->in[0] = c->in[1] = c->out[0] = c->out[1] = + c->err[0] = c->err[1] = c->fail[0] = c->fail[1] = + c->fds[0] = c->fds[1] = c->fds[2] = -1; + c->redirectErrorStream = false; + + rval = posix_spawn(&resultPid, helperpath, &file_actions, 0, (char * const *) hlpargs, environ); if (rval != 0) { return -1; @@ -666,6 +748,30 @@ startChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) } } +static int pipeSafely(int fd[2]) { + /* Pipe filedescriptors must be CLOEXEC as early as possible - ideally from the point of + * creation on - since at any moment a concurrent (third-party) fork() could inherit copies + * of these descriptors and accidentally keep the pipes open. That could cause the parent + * process to hang (see e.g. JDK-8377907). + * We use pipe2(2), if we have it. If we don't, we use pipe(2) + fcntl(2) immediately. + * The latter is still racy and can therefore still cause hangs as described in JDK-8377907, + * but at least the dangerous time window is as short as we can make it. + */ + int rc = -1; +#ifdef HAVE_PIPE2 + rc = pipe2(fd, O_CLOEXEC); +#else + rc = pipe(fd); + if (rc == 0) { + fcntl(fd[0], F_SETFD, FD_CLOEXEC); + fcntl(fd[1], F_SETFD, FD_CLOEXEC); + } +#endif /* HAVE_PIPE2 */ + assert(fdIsCloexec(fd[0])); + assert(fdIsCloexec(fd[1])); + return rc; +} + JNIEXPORT jint JNICALL Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, jobject process, @@ -678,7 +784,6 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, jintArray std_fds, jboolean redirectErrorStream) { - int errnum; int resultPid = -1; int in[2], out[2], err[2], fail[2], childenv[2]; jint *fds = NULL; @@ -727,11 +832,11 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, fds = (*env)->GetIntArrayElements(env, std_fds, NULL); if (fds == NULL) goto Catch; - if ((fds[0] == -1 && pipe(in) < 0) || - (fds[1] == -1 && pipe(out) < 0) || - (fds[2] == -1 && !redirectErrorStream && pipe(err) < 0) || // if not redirecting create the pipe - (pipe(childenv) < 0) || - (pipe(fail) < 0)) { + if ((fds[0] == -1 && pipeSafely(in) < 0) || + (fds[1] == -1 && pipeSafely(out) < 0) || + (fds[2] == -1 && !redirectErrorStream && pipeSafely(err) < 0) || + (pipeSafely(childenv) < 0) || + (pipeSafely(fail) < 0)) { throwInternalIOException(env, errno, "Bad file descriptor", mode); goto Catch; } @@ -782,9 +887,11 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, } close(fail[1]); fail[1] = -1; /* See: WhyCantJohnnyExec (childproc.c) */ + errcode_t errcode; + /* If we expect the child to ping aliveness, wait for it. */ if (c->sendAlivePing) { - switch(readFully(fail[0], &errnum, sizeof(errnum))) { + switch(readFully(fail[0], &errcode, sizeof(errcode))) { case 0: /* First exec failed; */ { int tmpStatus = 0; @@ -792,13 +899,15 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, throwExitCause(env, p, tmpStatus, c->mode); goto Catch; } - case sizeof(errnum): - if (errnum != CHILD_IS_ALIVE) { - /* This can happen if the spawn helper encounters an error - * before or during the handshake with the parent. */ - throwInternalIOException(env, 0, - "Bad code from spawn helper (Failed to exec spawn helper)", - c->mode); + case sizeof(errcode): + if (errcode.step != ESTEP_CHILD_ALIVE) { + /* This can happen if the child process encounters an error + * before or during initial handshake with the parent. */ + char msg[256]; + snprintf(msg, sizeof(msg), + "Bad early code from spawn helper " ERRCODE_FORMAT " (Failed to exec spawn helper)", + ERRCODE_FORMAT_ARGS(errcode)); + throwInternalIOException(env, 0, msg, c->mode); goto Catch; } break; @@ -808,11 +917,29 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, } } - switch (readFully(fail[0], &errnum, sizeof(errnum))) { + switch (readFully(fail[0], &errcode, sizeof(errcode))) { case 0: break; /* Exec succeeded */ - case sizeof(errnum): + case sizeof(errcode): + /* Always reap first! */ waitpid(resultPid, NULL, 0); - throwIOException(env, errnum, "Exec failed"); + /* Most of these errors are implementation errors and should result in an internal IOE, but + * a few can be caused by bad user input and need to be communicated to the end user. */ + switch(errcode.step) { + case ESTEP_CHDIR_FAIL: + throwIOException(env, errcode.errno_, "Failed to access working directory"); + break; + case ESTEP_EXEC_FAIL: + throwIOException(env, errcode.errno_, "Exec failed"); + break; + default: { + /* Probably implementation error */ + char msg[256]; + snprintf(msg, sizeof(msg), + "Bad code from spawn helper " ERRCODE_FORMAT " (Failed to exec spawn helper)", + ERRCODE_FORMAT_ARGS(errcode)); + throwInternalIOException(env, 0, msg, c->mode); + } + }; goto Catch; default: throwInternalIOException(env, errno, "Read failed", c->mode); diff --git a/src/java.base/unix/native/libjava/childproc.c b/src/java.base/unix/native/libjava/childproc.c index 9c6334e52d2..83ee782482f 100644 --- a/src/java.base/unix/native/libjava/childproc.c +++ b/src/java.base/unix/native/libjava/childproc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, 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 @@ -23,27 +23,56 @@ * questions. */ +#include #include #include #include #include +#include #include #include #include +#include #include #include #include "childproc.h" +#include "childproc_errorcodes.h" #include "jni_util.h" const char * const *parentPathv; +#ifdef DEBUG +bool fdIsValid(int fd) { + return fcntl(fd, F_GETFD) != -1; +} +bool fdIsPipe(int fd) { + struct stat buf; + errno = 0; + return fstat(fd, &buf) != -1 && S_ISFIFO(buf.st_mode); +} +bool fdIsCloexec(int fd) { + errno = 0; + const int flags = fcntl(fd, F_GETFD); + return flags != -1 && (flags & FD_CLOEXEC); +} +#endif // DEBUG + static int -restartableDup2(int fd_from, int fd_to) +restartableDup2(int fd_from, int fd_to, errcode_t* errcode) +/* All functions taking an errcode_t* as output behave the same: upon error, they populate + * errcode_t::hint and errcode_t::errno, but leave errcode_t::step as ESTEP_UNKNOWN since + * this information will be provided by the outer caller */ { int err; RESTARTABLE(dup2(fd_from, fd_to), err); - return err; + if (err == -1) { + /* use fd_to (the destination descriptor) as hint: it is a bit more telling + * than fd_from in our case */ + buildErrorCode(errcode, ESTEP_UNKNOWN, fd_to, errno); + return false; + } + return true; } int @@ -52,6 +81,16 @@ closeSafely(int fd) return (fd == -1) ? 0 : close(fd); } +/* Like closeSafely, but sets errcode (hint = fd, errno) on error and returns false */ +static bool +closeSafely2(int fd, errcode_t* errcode) { + if (closeSafely(fd) == -1) { + buildErrorCode(errcode, ESTEP_UNKNOWN, fd, errno); + return false; + } + return true; +} + int markCloseOnExec(int fd) { @@ -128,15 +167,19 @@ markDescriptorsCloseOnExec(void) return 0; } -static int -moveDescriptor(int fd_from, int fd_to) +static bool +moveDescriptor(int fd_from, int fd_to, errcode_t* errcode) { if (fd_from != fd_to) { - if ((restartableDup2(fd_from, fd_to) == -1) || - (close(fd_from) == -1)) - return -1; + if (!restartableDup2(fd_from, fd_to, errcode)) { + return false; + } + if (close(fd_from) == -1) { + buildErrorCode(errcode, ESTEP_UNKNOWN, fd_from, errno); + return false; + } } - return 0; + return true; } int @@ -367,54 +410,81 @@ int childProcess(void *arg) { const ChildStuff* p = (const ChildStuff*) arg; - int fail_pipe_fd = p->fail[1]; - if (p->sendAlivePing) { - /* Child shall signal aliveness to parent at the very first - * moment. */ - int code = CHILD_IS_ALIVE; - if (writeFully(fail_pipe_fd, &code, sizeof(code)) != sizeof(code)) { - goto WhyCantJohnnyExec; - } + int fail_pipe_fd = (p->mode == MODE_POSIX_SPAWN) ? + FAIL_FILENO : /* file descriptors already set up by posix_spawn(). */ + p->fail[1]; + + /* error information for WhyCantJohnnyExec */ + errcode_t errcode; + + /* Child shall signal aliveness to parent at the very first + * moment. */ + if (p->sendAlivePing && !sendAlivePing(fail_pipe_fd)) { + buildErrorCode(&errcode, ESTEP_SENDALIVE_FAIL, fail_pipe_fd, errno); + goto WhyCantJohnnyExec; } #ifdef DEBUG jtregSimulateCrash(0, 6); #endif - /* Close the parent sides of the pipes. - Closing pipe fds here is redundant, since markDescriptorsCloseOnExec() - would do it anyways, but a little paranoia is a good thing. */ - if ((closeSafely(p->in[1]) == -1) || - (closeSafely(p->out[0]) == -1) || - (closeSafely(p->err[0]) == -1) || - (closeSafely(p->childenv[0]) == -1) || - (closeSafely(p->childenv[1]) == -1) || - (closeSafely(p->fail[0]) == -1)) - goto WhyCantJohnnyExec; - /* Give the child sides of the pipes the right fileno's. */ - /* Note: it is possible for in[0] == 0 */ - if ((moveDescriptor(p->in[0] != -1 ? p->in[0] : p->fds[0], - STDIN_FILENO) == -1) || - (moveDescriptor(p->out[1]!= -1 ? p->out[1] : p->fds[1], - STDOUT_FILENO) == -1)) - goto WhyCantJohnnyExec; + /* File descriptor setup for non-Posix-spawn mode */ + if (p->mode != MODE_POSIX_SPAWN) { - if (p->redirectErrorStream) { - if ((closeSafely(p->err[1]) == -1) || - (restartableDup2(STDOUT_FILENO, STDERR_FILENO) == -1)) + /* Close the parent sides of the pipes. + Closing pipe fds here is redundant, since markDescriptorsCloseOnExec() + would do it anyways, but a little paranoia is a good thing. */ + if (!closeSafely2(p->in[1], &errcode) || + !closeSafely2(p->out[0], &errcode) || + !closeSafely2(p->err[0], &errcode) || + !closeSafely2(p->childenv[0], &errcode) || + !closeSafely2(p->childenv[1], &errcode) || + !closeSafely2(p->fail[0], &errcode)) + { + errcode.step = ESTEP_PIPECLOSE_FAIL; goto WhyCantJohnnyExec; - } else { - if (moveDescriptor(p->err[1] != -1 ? p->err[1] : p->fds[2], - STDERR_FILENO) == -1) + } + + /* Give the child sides of the pipes the right fileno's. */ + /* Note: it is possible for in[0] == 0 */ + if (!moveDescriptor(p->in[0] != -1 ? p->in[0] : p->fds[0], + STDIN_FILENO, &errcode)) { + errcode.step = ESTEP_DUP2_STDIN_FAIL; goto WhyCantJohnnyExec; - } + } - if (moveDescriptor(fail_pipe_fd, FAIL_FILENO) == -1) - goto WhyCantJohnnyExec; + if (!moveDescriptor(p->out[1] != -1 ? p->out[1] : p->fds[1], + STDOUT_FILENO, &errcode)) { + errcode.step = ESTEP_DUP2_STDOUT_FAIL; + goto WhyCantJohnnyExec; + } - /* We moved the fail pipe fd */ - fail_pipe_fd = FAIL_FILENO; + if (p->redirectErrorStream) { + if (!closeSafely2(p->err[1], &errcode) || + !restartableDup2(STDOUT_FILENO, STDERR_FILENO, &errcode)) { + errcode.step = ESTEP_DUP2_STDERR_REDIRECT_FAIL; + goto WhyCantJohnnyExec; + } + } else { + if (!moveDescriptor(p->err[1] != -1 ? p->err[1] : p->fds[2], + STDERR_FILENO, &errcode)) { + errcode.step = ESTEP_DUP2_STDERR_REDIRECT_FAIL; + goto WhyCantJohnnyExec; + } + } + + if (!moveDescriptor(fail_pipe_fd, FAIL_FILENO, &errcode)) { + errcode.step = ESTEP_DUP2_FAILPIPE_FAIL; + goto WhyCantJohnnyExec; + } + + /* We moved the fail pipe fd */ + fail_pipe_fd = FAIL_FILENO; + + } /* end: FORK/VFORK mode */ + + assert(fail_pipe_fd == FAIL_FILENO); /* For AIX: The code in markDescriptorsCloseOnExec() relies on the current * semantic of this function. When this point here is reached only the @@ -424,14 +494,19 @@ childProcess(void *arg) if (markDescriptorsCloseOnExec() == -1) { /* failed, close the old way */ int max_fd = (int)sysconf(_SC_OPEN_MAX); int fd; - for (fd = STDERR_FILENO + 1; fd < max_fd; fd++) - if (markCloseOnExec(fd) == -1 && errno != EBADF) + for (fd = STDERR_FILENO + 1; fd < max_fd; fd++) { + if (markCloseOnExec(fd) == -1 && errno != EBADF) { + buildErrorCode(&errcode, ESTEP_CLOEXEC_FAIL, fd, errno); goto WhyCantJohnnyExec; + } + } } /* change to the new working directory */ - if (p->pdir != NULL && chdir(p->pdir) < 0) + if (p->pdir != NULL && chdir(p->pdir) < 0) { + buildErrorCode(&errcode, ESTEP_CHDIR_FAIL, 0, errno); goto WhyCantJohnnyExec; + } // Reset any mask signals from parent, but not in VFORK mode if (p->mode != MODE_VFORK) { @@ -442,28 +517,32 @@ childProcess(void *arg) // Children should be started with default signal disposition for SIGPIPE if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) { + buildErrorCode(&errcode, ESTEP_SET_SIGPIPE, 0, errno); goto WhyCantJohnnyExec; } JDK_execvpe(p->mode, p->argv[0], p->argv, p->envv); + /* Still here. Hmm. */ + buildErrorCode(&errcode, ESTEP_EXEC_FAIL, 0, errno); + WhyCantJohnnyExec: /* We used to go to an awful lot of trouble to predict whether the * child would fail, but there is no reliable way to predict the * success of an operation without *trying* it, and there's no way * to try a chdir or exec in the parent. Instead, all we need is a * way to communicate any failure back to the parent. Easy; we just - * send the errno back to the parent over a pipe in case of failure. + * send the errorcode back to the parent over a pipe in case of failure. * The tricky thing is, how do we communicate the *success* of exec? * We use FD_CLOEXEC together with the fact that a read() on a pipe * yields EOF when the write ends (we have two of them!) are closed. */ - { - int errnum = errno; - writeFully(fail_pipe_fd, &errnum, sizeof(errnum)); + if (!sendErrorCode(fail_pipe_fd, errcode)) { + printf("childproc fail: " ERRCODE_FORMAT "\n", ERRCODE_FORMAT_ARGS(errcode)); } + int exitcode = exitCodeFromErrorCode(errcode); close(fail_pipe_fd); - _exit(-1); + _exit(exitcode); return 0; /* Suppress warning "no return value from function" */ } diff --git a/src/java.base/unix/native/libjava/childproc.h b/src/java.base/unix/native/libjava/childproc.h index 974fac3bddd..27414e60137 100644 --- a/src/java.base/unix/native/libjava/childproc.h +++ b/src/java.base/unix/native/libjava/childproc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, 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,6 +27,7 @@ #define CHILDPROC_MD_H #include +#include #ifdef __APPLE__ #include @@ -72,6 +73,9 @@ extern char **environ; #define FAIL_FILENO (STDERR_FILENO + 1) +/* For POSIX_SPAWN mode */ +#define CHILDENV_FILENO (FAIL_FILENO + 1) + /* These numbers must be the same as the Enum in ProcessImpl.java * Must be a better way of doing this. */ @@ -107,13 +111,6 @@ typedef struct _SpawnInfo { int parentPathvBytes; /* total number of bytes in parentPathv array */ } SpawnInfo; -/* If ChildStuff.sendAlivePing is true, child shall signal aliveness to - * the parent the moment it gains consciousness, before any subsequent - * pre-exec errors could happen. - * This code must fit into an int and not be a valid errno value on any of - * our platforms. */ -#define CHILD_IS_ALIVE 65535 - /** * The cached and split version of the JDK's effective PATH. * (We don't support putenv("PATH=...") in native code) @@ -136,6 +133,17 @@ int childProcess(void *arg); * See: test/jdk/java/lang/ProcessBuilder/JspawnhelperProtocol.java */ void jtregSimulateCrash(pid_t child, int stage); +/* Helper functions to check the state of fds */ +bool fdIsValid(int fd); +bool fdIsPipe(int fd); +bool fdIsCloexec(int fd); #endif +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +#define HAVE_PIPE2 +#else +// Neither MacOS nor AIX support pipe2, unfortunately +#undef HAVE_PIPE2 #endif + +#endif /* CHILDPROC_MD_H */ diff --git a/src/java.base/unix/native/libjava/childproc_errorcodes.c b/src/java.base/unix/native/libjava/childproc_errorcodes.c new file mode 100644 index 00000000000..4dc8e927616 --- /dev/null +++ b/src/java.base/unix/native/libjava/childproc_errorcodes.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2026, 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. + */ + +#include +#include + +#include +#include "childproc.h" +#include "childproc_errorcodes.h" + +void buildErrorCode(errcode_t* errcode, int step, int hint, int errno_) { + errcode_t e; + + assert(step < (1 << 8)); + e.step = step; + + assert(errno_ < (1 << 8)); + e.errno_ = errno_; + + const int maxhint = (1 << 16); + e.hint = hint < maxhint ? hint : maxhint; + + (*errcode) = e; +} + +int exitCodeFromErrorCode(errcode_t errcode) { + /* We use the fail step number as exit code, but avoid 0 and 1 + * and try to avoid the [128..256) range since that one is used by + * shells to codify abnormal kills by signal. */ + return 0x10 + errcode.step; +} + +bool sendErrorCode(int fd, errcode_t errcode) { + return writeFully(fd, &errcode, sizeof(errcode)) == sizeof(errcode); +} + +bool sendAlivePing(int fd) { + errcode_t errcode; + buildErrorCode(&errcode, ESTEP_CHILD_ALIVE, getpid(), 0); + return sendErrorCode(fd, errcode); +} diff --git a/src/java.base/unix/native/libjava/childproc_errorcodes.h b/src/java.base/unix/native/libjava/childproc_errorcodes.h new file mode 100644 index 00000000000..8379db4ad2b --- /dev/null +++ b/src/java.base/unix/native/libjava/childproc_errorcodes.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2026, 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. + */ + +#ifndef CHILDPROC_ERRORCODES_H +#define CHILDPROC_ERRORCODES_H + +#include +#include + +typedef struct errcode_t_ { + unsigned step : 8; + unsigned hint : 16; + unsigned errno_ : 8; +} errcode_t; + +/* Helper macros for printing an errcode_t */ +#define ERRCODE_FORMAT "(%u-%u-%u)" +#define ERRCODE_FORMAT_ARGS(errcode) errcode.step, errcode.hint, errcode.errno_ + + +/* Builds up an error code. + * Note: + * - hint will be capped at 2^16 + * - both step and errno_ must fit into 8 bits. */ +void buildErrorCode(errcode_t* errcode, int step, int hint, int errno_); + +/* Sends an error code down a pipe. Returns true if sent successfully. */ +bool sendErrorCode(int fd, errcode_t errcode); + +/* Build an exit code for an errcode (used as child process exit code + * in addition to the errcode being sent to parent). */ +int exitCodeFromErrorCode(errcode_t errcode); + +/* Sends alive ping down a pipe. Returns true if sent successfully. */ +bool sendAlivePing(int fd); + +#define ESTEP_UNKNOWN 0 + +/* not an error code, but an "I am alive" ping from the child. + * hint is child pid, errno is 0. */ +#define ESTEP_CHILD_ALIVE 255 + +/* JspawnHelper */ +#define ESTEP_JSPAWN_ARG_ERROR 1 +#define ESTEP_JSPAWN_VERSION_ERROR 2 + +/* Checking file descriptor setup + * hint is the (16-bit-capped) fd number */ +#define ESTEP_JSPAWN_INVALID_FD 3 +#define ESTEP_JSPAWN_NOT_A_PIPE 4 + +/* Allocation fail in jspawnhelper. + * hint is the (16-bit-capped) fail size */ +#define ESTEP_JSPAWN_ALLOC_FAILED 5 + +/* Receiving Childstuff from parent, communication error. + * hint is the substep. */ +#define ESTEP_JSPAWN_RCV_CHILDSTUFF_COMM_FAIL 6 + +/* Expand if needed ... */ + +/* childproc() */ + +/* Failed to send aliveness ping + * hint is the (16-bit-capped) fd. */ +#define ESTEP_SENDALIVE_FAIL 10 + +/* Failed to close a pipe in fork mode + * hint is the (16-bit-capped) fd. */ +#define ESTEP_PIPECLOSE_FAIL 11 + +/* Failed to dup2 a file descriptor in fork mode. + * hint is the (16-bit-capped) fd_to (!) */ +#define ESTEP_DUP2_STDIN_FAIL 13 +#define ESTEP_DUP2_STDOUT_FAIL 14 +#define ESTEP_DUP2_STDERR_REDIRECT_FAIL 15 +#define ESTEP_DUP2_STDERR_FAIL 16 +#define ESTEP_DUP2_FAILPIPE_FAIL 17 + +/* Failed to mark a file descriptor as CLOEXEC + * hint is the (16-bit-capped) fd */ +#define ESTEP_CLOEXEC_FAIL 18 + +/* Failed to chdir into the target working directory */ +#define ESTEP_CHDIR_FAIL 19 + +/* Failed to change signal disposition for SIGPIPE to default */ +#define ESTEP_SET_SIGPIPE 20 + +/* Expand if needed ... */ + +/* All modes: exec() failed */ +#define ESTEP_EXEC_FAIL 30 + +#endif /* CHILDPROC_MD_H */ diff --git a/src/java.desktop/aix/native/libawt/porting_aix.c b/src/java.desktop/aix/native/libawt/porting_aix.c index b506ef5a44b..d8688c212d7 100644 --- a/src/java.desktop/aix/native/libawt/porting_aix.c +++ b/src/java.desktop/aix/native/libawt/porting_aix.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023 SAP SE. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. 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 @@ -48,15 +48,14 @@ static int dladdr_dont_reload(void* addr, Dl_info* info) { if (addr >= p->ldinfo_textorg && (char*)addr < (char*)(p->ldinfo_textorg) + p->ldinfo_textsize) { info->dli_fname = p->ldinfo_filename; - info->dli_fbase = p->ldinfo_textorg; - return 1; /* [sic] */ + return 1; } if (!p->ldinfo_next) { break; } p = (struct ld_info*)(((char*)p) + p->ldinfo_next); } - return 0; /* [sic] */ + return 0; } #ifdef __cplusplus @@ -69,14 +68,14 @@ int dladdr(void *addr, Dl_info *info) { loaded = 1; } if (!addr) { - return 0; /* [sic] */ + return 0; } /* Address could be AIX function descriptor? */ void* const addr0 = *( (void**) addr ); int rc = dladdr_dont_reload(addr, info); if (rc == 0) { rc = dladdr_dont_reload(addr0, info); - if (rc == 0) { /* [sic] */ + if (rc == 0) { fill_dll_info(); /* refill, maybe loadquery info is outdated */ rc = dladdr_dont_reload(addr, info); if (rc == 0) { diff --git a/src/java.desktop/aix/native/libawt/porting_aix.h b/src/java.desktop/aix/native/libawt/porting_aix.h index 719bbaf224e..04d11590915 100644 --- a/src/java.desktop/aix/native/libawt/porting_aix.h +++ b/src/java.desktop/aix/native/libawt/porting_aix.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018 SAP SE. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. 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 @@ -24,36 +24,15 @@ * */ -/* - * Header file to contain porting-relevant code which does not have a - * home anywhere else. - * This is initially based on hotspot/src/os/aix/vm/{loadlib,porting}_aix.{hpp,cpp} - */ - /* * Aix' own version of dladdr(). * This function tries to mimic dladdr(3) on Linux * (see http://linux.die.net/man/3/dladdr) * dladdr(3) is not POSIX but a GNU extension, and is not available on AIX. * - * Differences between AIX dladdr and Linux dladdr: - * - * 1) Dl_info.dli_fbase: can never work, is disabled. - * A loaded image on AIX is divided in multiple segments, at least two - * (text and data) but potentially also far more. This is because the loader may - * load each member into an own segment, as for instance happens with the libC.a - * 2) Dl_info.dli_sname: This only works for code symbols (functions); for data, a - * zero-length string is returned (""). - * 3) Dl_info.dli_saddr: For code, this will return the entry point of the function, - * not the function descriptor. */ -typedef struct { - const char *dli_fname; /* file path of loaded library */ - void *dli_fbase; /* doesn't make sense on AIX */ - const char *dli_sname; /* symbol name; "" if not known */ - void *dli_saddr; /* address of *entry* of function; not function descriptor; */ -} Dl_info; +#include "dl_info.h" #ifdef __cplusplus extern "C" diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m index 460749c363d..faacef5adea 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m @@ -70,6 +70,7 @@ JNI_COCOA_ENTER(env); dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC)); // 1 second timeout // Asynchronous call to openURL + dispatch_retain(semaphore); [[NSWorkspace sharedWorkspace] openURLs:urls withApplicationAtURL:appURI configuration:configuration @@ -78,9 +79,11 @@ JNI_COCOA_ENTER(env); status = (OSStatus) error.code; } dispatch_semaphore_signal(semaphore); + dispatch_release(semaphore); }]; dispatch_semaphore_wait(semaphore, timeout); + dispatch_release(semaphore); JNI_COCOA_EXIT(env); return status; @@ -146,6 +149,7 @@ JNI_COCOA_ENTER(env); dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC)); // 1 second timeout // Asynchronous call - openURLs:withApplicationAtURL + dispatch_retain(semaphore); [[NSWorkspace sharedWorkspace] openURLs:urls withApplicationAtURL:appURI configuration:configuration @@ -154,9 +158,11 @@ JNI_COCOA_ENTER(env); status = (OSStatus) error.code; } dispatch_semaphore_signal(semaphore); + dispatch_release(semaphore); }]; dispatch_semaphore_wait(semaphore, timeout); + dispatch_release(semaphore); [urlToOpen release]; JNI_COCOA_EXIT(env); diff --git a/src/java.desktop/share/classes/java/awt/Desktop.java b/src/java.desktop/share/classes/java/awt/Desktop.java index 3f73fa6cd81..43bd1f9c11c 100644 --- a/src/java.desktop/share/classes/java/awt/Desktop.java +++ b/src/java.desktop/share/classes/java/awt/Desktop.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -272,6 +272,8 @@ public class Desktop { } } + private static Desktop desktop; + /** * Returns the {@code Desktop} instance of the current * desktop context. On some platforms the Desktop API may not be @@ -292,12 +294,8 @@ public class Desktop { "supported on the current platform"); } - sun.awt.AppContext context = sun.awt.AppContext.getAppContext(); - Desktop desktop = (Desktop)context.get(Desktop.class); - if (desktop == null) { desktop = new Desktop(); - context.put(Desktop.class, desktop); } return desktop; diff --git a/src/java.desktop/share/classes/java/awt/SequencedEvent.java b/src/java.desktop/share/classes/java/awt/SequencedEvent.java index 13ec5317822..25fe72e1787 100644 --- a/src/java.desktop/share/classes/java/awt/SequencedEvent.java +++ b/src/java.desktop/share/classes/java/awt/SequencedEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -29,12 +29,11 @@ import java.io.Serial; import java.util.LinkedList; import sun.awt.AWTAccessor; -import sun.awt.AppContext; import sun.awt.SunToolkit; /** * A mechanism for ensuring that a series of AWTEvents are executed in a - * precise order, even across multiple AppContexts. The nested events will be + * precise order. The nested events will be * dispatched in the order in which their wrapping SequencedEvents were * constructed. The only exception to this rule is if the peer of the target of * the nested event was destroyed (with a call to Component.removeNotify) @@ -57,7 +56,6 @@ class SequencedEvent extends AWTEvent implements ActiveEvent { private final AWTEvent nested; @SuppressWarnings("serial") // Not statically typed as Serializable - private AppContext appContext; private boolean disposed; private final LinkedList pendingEvents = new LinkedList<>(); @@ -145,7 +143,6 @@ class SequencedEvent extends AWTEvent implements ActiveEvent { * dispatched or disposed. If this method is invoked before all previous nested events * have been dispatched, then this method blocks until such a point is * reached. - * While waiting disposes nested events to disposed AppContext * * NOTE: Locking protocol. Since dispose() can get EventQueue lock, * dispatch() shall never call dispose() while holding the lock on the list, @@ -154,8 +151,6 @@ class SequencedEvent extends AWTEvent implements ActiveEvent { */ public final void dispatch() { try { - appContext = AppContext.getAppContext(); - if (getFirst() != this) { if (EventQueue.isDispatchThread()) { if (Thread.currentThread() instanceof EventDispatchThread) { @@ -201,19 +196,6 @@ class SequencedEvent extends AWTEvent implements ActiveEvent { } } - /** - * true only if event exists and nested source appContext is disposed. - */ - private static final boolean isOwnerAppContextDisposed(SequencedEvent se) { - if (se != null) { - Object target = se.nested.getSource(); - if (target instanceof Component) { - return ((Component)target).appContext.isDisposed(); - } - } - return false; - } - /** * Sequenced events are dispatched in order, so we cannot dispatch * until we are the first sequenced event in the queue (i.e. it's our @@ -224,26 +206,13 @@ class SequencedEvent extends AWTEvent implements ActiveEvent { if (disposed) { return true; } - // getFirstWithContext can dispose this - return this == getFirstWithContext() || disposed; + return this == getFirst(); } private static final synchronized SequencedEvent getFirst() { return list.getFirst(); } - /* Disposes all events from disposed AppContext - * return first valid event - */ - private static final SequencedEvent getFirstWithContext() { - SequencedEvent first = getFirst(); - while(isOwnerAppContextDisposed(first)) { - first.dispose(); - first = getFirst(); - } - return first; - } - /** * Disposes of this instance. This method is invoked once the nested event * has been dispatched and handled, or when the peer of the target of the @@ -283,12 +252,12 @@ class SequencedEvent extends AWTEvent implements ActiveEvent { } } // Wake up waiting threads - if (next != null && next.appContext != null) { - SunToolkit.postEvent(next.appContext, new SentEvent()); + if (next != null) { + SunToolkit.postEvent(new SentEvent()); } for(AWTEvent e : pendingEvents) { - SunToolkit.postEvent(appContext, e); + SunToolkit.postEvent(e); } } } diff --git a/src/java.desktop/share/classes/java/awt/Toolkit.java b/src/java.desktop/share/classes/java/awt/Toolkit.java index 1ca5cdd8112..4fc9dc8e82d 100644 --- a/src/java.desktop/share/classes/java/awt/Toolkit.java +++ b/src/java.desktop/share/classes/java/awt/Toolkit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2026, 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 @@ -72,7 +72,6 @@ import java.util.stream.Collectors; import javax.accessibility.AccessibilityProvider; import sun.awt.AWTAccessor; -import sun.awt.AppContext; import sun.awt.HeadlessToolkit; import sun.awt.PeerEvent; import sun.awt.PlatformGraphicsInfo; @@ -2046,8 +2045,6 @@ public abstract class Toolkit { @SuppressWarnings("serial") private static class DesktopPropertyChangeSupport extends PropertyChangeSupport { - private static final StringBuilder PROP_CHANGE_SUPPORT_KEY = - new StringBuilder("desktop property change support key"); private final Object source; public DesktopPropertyChangeSupport(Object sourceBean) { @@ -2055,16 +2052,14 @@ public abstract class Toolkit { source = sourceBean; } + private static PropertyChangeSupport pcs; @Override public synchronized void addPropertyChangeListener( String propertyName, PropertyChangeListener listener) { - PropertyChangeSupport pcs = (PropertyChangeSupport) - AppContext.getAppContext().get(PROP_CHANGE_SUPPORT_KEY); if (null == pcs) { pcs = new PropertyChangeSupport(source); - AppContext.getAppContext().put(PROP_CHANGE_SUPPORT_KEY, pcs); } pcs.addPropertyChangeListener(propertyName, listener); } @@ -2074,8 +2069,6 @@ public abstract class Toolkit { String propertyName, PropertyChangeListener listener) { - PropertyChangeSupport pcs = (PropertyChangeSupport) - AppContext.getAppContext().get(PROP_CHANGE_SUPPORT_KEY); if (null != pcs) { pcs.removePropertyChangeListener(propertyName, listener); } @@ -2084,8 +2077,6 @@ public abstract class Toolkit { @Override public synchronized PropertyChangeListener[] getPropertyChangeListeners() { - PropertyChangeSupport pcs = (PropertyChangeSupport) - AppContext.getAppContext().get(PROP_CHANGE_SUPPORT_KEY); if (null != pcs) { return pcs.getPropertyChangeListeners(); } else { @@ -2096,8 +2087,6 @@ public abstract class Toolkit { @Override public synchronized PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { - PropertyChangeSupport pcs = (PropertyChangeSupport) - AppContext.getAppContext().get(PROP_CHANGE_SUPPORT_KEY); if (null != pcs) { return pcs.getPropertyChangeListeners(propertyName); } else { @@ -2107,19 +2096,14 @@ public abstract class Toolkit { @Override public synchronized void addPropertyChangeListener(PropertyChangeListener listener) { - PropertyChangeSupport pcs = (PropertyChangeSupport) - AppContext.getAppContext().get(PROP_CHANGE_SUPPORT_KEY); if (null == pcs) { pcs = new PropertyChangeSupport(source); - AppContext.getAppContext().put(PROP_CHANGE_SUPPORT_KEY, pcs); } pcs.addPropertyChangeListener(listener); } @Override public synchronized void removePropertyChangeListener(PropertyChangeListener listener) { - PropertyChangeSupport pcs = (PropertyChangeSupport) - AppContext.getAppContext().get(PROP_CHANGE_SUPPORT_KEY); if (null != pcs) { pcs.removePropertyChangeListener(listener); } @@ -2131,33 +2115,16 @@ public abstract class Toolkit { */ @Override public void firePropertyChange(final PropertyChangeEvent evt) { + if (pcs == null) { + return; + } Object oldValue = evt.getOldValue(); Object newValue = evt.getNewValue(); String propertyName = evt.getPropertyName(); if (oldValue != null && newValue != null && oldValue.equals(newValue)) { return; } - Runnable updater = new Runnable() { - public void run() { - PropertyChangeSupport pcs = (PropertyChangeSupport) - AppContext.getAppContext().get(PROP_CHANGE_SUPPORT_KEY); - if (null != pcs) { - pcs.firePropertyChange(evt); - } - } - }; - final AppContext currentAppContext = AppContext.getAppContext(); - for (AppContext appContext : AppContext.getAppContexts()) { - if (null == appContext || appContext.isDisposed()) { - continue; - } - if (currentAppContext == appContext) { - updater.run(); - } else { - final PeerEvent e = new PeerEvent(source, updater, PeerEvent.ULTIMATE_PRIORITY_EVENT); - SunToolkit.postEvent(appContext, e); - } - } + pcs.firePropertyChange(evt); } } diff --git a/src/java.desktop/share/classes/java/awt/event/InputMethodEvent.java b/src/java.desktop/share/classes/java/awt/event/InputMethodEvent.java index 12ad1a03171..429d0b819f4 100644 --- a/src/java.desktop/share/classes/java/awt/event/InputMethodEvent.java +++ b/src/java.desktop/share/classes/java/awt/event/InputMethodEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -37,7 +37,6 @@ import java.text.AttributedCharacterIterator; import java.text.CharacterIterator; import sun.awt.AWTAccessor; -import sun.awt.AppContext; import sun.awt.SunToolkit; /** @@ -444,8 +443,7 @@ public class InputMethodEvent extends AWTEvent { // throw the IllegalArgumentException to conform to EventObject spec throw new IllegalArgumentException("null source"); } - AppContext appContext = SunToolkit.targetToAppContext(source); - EventQueue eventQueue = SunToolkit.getSystemEventQueueImplPP(appContext); + EventQueue eventQueue = SunToolkit.getSystemEventQueueImplPP(); return AWTAccessor.getEventQueueAccessor().getMostRecentEventTime(eventQueue); } } diff --git a/src/java.desktop/share/classes/java/awt/event/WindowEvent.java b/src/java.desktop/share/classes/java/awt/event/WindowEvent.java index d898860f153..b2dbdb4f13f 100644 --- a/src/java.desktop/share/classes/java/awt/event/WindowEvent.java +++ b/src/java.desktop/share/classes/java/awt/event/WindowEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, 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 @@ -29,7 +29,6 @@ import java.awt.Window; import java.io.Serial; import java.lang.annotation.Native; -import sun.awt.AppContext; import sun.awt.SunToolkit; /** @@ -324,21 +323,14 @@ public class WindowEvent extends ComponentEvent { * WINDOW_LOST_FOCUS event, this is the Window that gained activation or * focus. For any other type of WindowEvent, or if the focus or activation * change occurs with a native application, with a Java application in a - * different VM or context, or with no other Window, null is returned. + * different VM, or with no other Window, null is returned. * * @return the other Window involved in the focus or activation change, or * null * @since 1.4 */ public Window getOppositeWindow() { - if (opposite == null) { - return null; - } - - return (SunToolkit.targetToAppContext(opposite) == - AppContext.getAppContext()) - ? opposite - : null; + return opposite; } /** diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/Media.java b/src/java.desktop/share/classes/javax/print/attribute/standard/Media.java index 6b62f1dbecd..0cb9a4c30c5 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/Media.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/Media.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -89,6 +89,7 @@ public abstract class Media extends EnumSyntax * @return {@code true} if {@code object} is equivalent to this media * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return object instanceof Media other && object.getClass() == this.getClass() && @@ -105,6 +106,7 @@ public abstract class Media extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return Media.class; } @@ -118,6 +120,7 @@ public abstract class Media extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "media"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaName.java b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaName.java index 9c1b4060a34..4adb2839759 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaName.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -107,6 +107,7 @@ public class MediaName extends Media implements Attribute { * * @return the string table */ + @Override protected String[] getStringTable() { return myStringTable.clone(); @@ -117,6 +118,7 @@ public class MediaName extends Media implements Attribute { * * @return the enumeration value table */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaPrintableArea.java b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaPrintableArea.java index 72a58f2b697..a9cc2bba195 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaPrintableArea.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaPrintableArea.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -241,6 +241,7 @@ public final class MediaPrintableArea * @return {@code true} if {@code object} is equivalent to this media * margins attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { boolean ret = false; if (object instanceof MediaPrintableArea) { @@ -262,6 +263,7 @@ public final class MediaPrintableArea * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return MediaPrintableArea.class; } @@ -277,6 +279,7 @@ public final class MediaPrintableArea * * @return attribute category name */ + @Override public final String getName() { return "media-printable-area"; } @@ -304,6 +307,7 @@ public final class MediaPrintableArea /** * Returns a string version of this rectangular size attribute in mm. */ + @Override public String toString() { return(toString(MM, "mm")); } @@ -311,6 +315,7 @@ public final class MediaPrintableArea /** * Returns a hash code value for this attribute. */ + @Override public int hashCode() { return x + 37*y + 43*w + 47*h; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSize.java b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSize.java index 6a8db3b94c2..57c0d305809 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSize.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -243,6 +243,7 @@ public class MediaSize extends Size2DSyntax implements Attribute { * @return {@code true} if {@code object} is equivalent to this media size * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof MediaSize); } @@ -257,6 +258,7 @@ public class MediaSize extends Size2DSyntax implements Attribute { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return MediaSize.class; } @@ -270,6 +272,7 @@ public class MediaSize extends Size2DSyntax implements Attribute { * * @return attribute category name */ + @Override public final String getName() { return "media-size"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSizeName.java b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSizeName.java index f9dde495610..52b60b046bf 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSizeName.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSizeName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -608,6 +608,7 @@ public class MediaSizeName extends Media { /** * Returns the string table for class {@code MediaSizeName}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); @@ -616,6 +617,7 @@ public class MediaSizeName extends Media { /** * Returns the enumeration value table for class {@code MediaSizeName}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaTray.java b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaTray.java index 138aae6248a..bc20cbb8901 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaTray.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaTray.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -132,6 +132,7 @@ public class MediaTray extends Media implements Attribute { /** * Returns the string table for class {@code MediaTray}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); @@ -140,6 +141,7 @@ public class MediaTray extends Media implements Attribute { /** * Returns the enumeration value table for class {@code MediaTray}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/MultipleDocumentHandling.java b/src/java.desktop/share/classes/javax/print/attribute/standard/MultipleDocumentHandling.java index 6d12597c88a..4937f379684 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/MultipleDocumentHandling.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/MultipleDocumentHandling.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -219,6 +219,7 @@ public class MultipleDocumentHandling extends EnumSyntax /** * Returns the string table for class {@code MultipleDocumentHandling}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); } @@ -227,6 +228,7 @@ public class MultipleDocumentHandling extends EnumSyntax * Returns the enumeration value table for class * {@code MultipleDocumentHandling}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } @@ -242,6 +244,7 @@ public class MultipleDocumentHandling extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return MultipleDocumentHandling.class; } @@ -255,6 +258,7 @@ public class MultipleDocumentHandling extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "multiple-document-handling"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/NumberOfDocuments.java b/src/java.desktop/share/classes/javax/print/attribute/standard/NumberOfDocuments.java index 18b8144fb89..901907620f9 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/NumberOfDocuments.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/NumberOfDocuments.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -77,6 +77,7 @@ public final class NumberOfDocuments extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this number of * documents attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof NumberOfDocuments); @@ -92,6 +93,7 @@ public final class NumberOfDocuments extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return NumberOfDocuments.class; } @@ -105,6 +107,7 @@ public final class NumberOfDocuments extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "number-of-documents"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/NumberOfInterveningJobs.java b/src/java.desktop/share/classes/javax/print/attribute/standard/NumberOfInterveningJobs.java index 54cb4b85e7a..480de3b1b47 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/NumberOfInterveningJobs.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/NumberOfInterveningJobs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -78,6 +78,7 @@ public final class NumberOfInterveningJobs extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this number of * intervening jobs attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof NumberOfInterveningJobs); @@ -93,6 +94,7 @@ public final class NumberOfInterveningJobs extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return NumberOfInterveningJobs.class; } @@ -106,6 +108,7 @@ public final class NumberOfInterveningJobs extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "number-of-intervening-jobs"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/NumberUp.java b/src/java.desktop/share/classes/javax/print/attribute/standard/NumberUp.java index b3aabbc6cfd..0b019346c8e 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/NumberUp.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/NumberUp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -145,6 +145,7 @@ public final class NumberUp extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this number up * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof NumberUp); } @@ -159,6 +160,7 @@ public final class NumberUp extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return NumberUp.class; } @@ -171,6 +173,7 @@ public final class NumberUp extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "number-up"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/NumberUpSupported.java b/src/java.desktop/share/classes/javax/print/attribute/standard/NumberUpSupported.java index dfb79835e0f..cef65ed17a3 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/NumberUpSupported.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/NumberUpSupported.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -136,6 +136,7 @@ public final class NumberUpSupported extends SetOfIntegerSyntax * @return {@code true} if {@code object} is equivalent to this number up * supported attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof NumberUpSupported); @@ -151,6 +152,7 @@ public final class NumberUpSupported extends SetOfIntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return NumberUpSupported.class; } @@ -164,6 +166,7 @@ public final class NumberUpSupported extends SetOfIntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "number-up-supported"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/OrientationRequested.java b/src/java.desktop/share/classes/javax/print/attribute/standard/OrientationRequested.java index 253adfe1ff1..00e3de4a1de 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/OrientationRequested.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/OrientationRequested.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -150,6 +150,7 @@ public final class OrientationRequested extends EnumSyntax /** * Returns the string table for class {@code OrientationRequested}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -158,6 +159,7 @@ public final class OrientationRequested extends EnumSyntax * Returns the enumeration value table for class * {@code OrientationRequested}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -166,6 +168,7 @@ public final class OrientationRequested extends EnumSyntax * Returns the lowest integer value used by class * {@code OrientationRequested}. */ + @Override protected int getOffset() { return 3; } @@ -180,6 +183,7 @@ public final class OrientationRequested extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return OrientationRequested.class; } @@ -193,6 +197,7 @@ public final class OrientationRequested extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "orientation-requested"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/OutputDeviceAssigned.java b/src/java.desktop/share/classes/javax/print/attribute/standard/OutputDeviceAssigned.java index c94a105e92d..bea5cd35060 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/OutputDeviceAssigned.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/OutputDeviceAssigned.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -92,6 +92,7 @@ public final class OutputDeviceAssigned extends TextSyntax * @return {@code true} if {@code object} is equivalent to this output * device assigned attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof OutputDeviceAssigned); @@ -107,6 +108,7 @@ public final class OutputDeviceAssigned extends TextSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return OutputDeviceAssigned.class; } @@ -120,6 +122,7 @@ public final class OutputDeviceAssigned extends TextSyntax * * @return attribute category name */ + @Override public final String getName() { return "output-device-assigned"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PDLOverrideSupported.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PDLOverrideSupported.java index 46b9e8493d3..d9862ff633b 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PDLOverrideSupported.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PDLOverrideSupported.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -97,6 +97,7 @@ public class PDLOverrideSupported extends EnumSyntax /** * Returns the string table for class {@code PDLOverrideSupported}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); } @@ -105,6 +106,7 @@ public class PDLOverrideSupported extends EnumSyntax * Returns the enumeration value table for class * {@code PDLOverrideSupported}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } @@ -119,6 +121,7 @@ public class PDLOverrideSupported extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PDLOverrideSupported.class; } @@ -132,6 +135,7 @@ public class PDLOverrideSupported extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "pdl-override-supported"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PageRanges.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PageRanges.java index 3ba0fa8e07e..74325a3eb2f 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PageRanges.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PageRanges.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -209,6 +209,7 @@ public final class PageRanges extends SetOfIntegerSyntax * @return {@code true} if {@code object} is equivalent to this page ranges * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PageRanges); } @@ -223,6 +224,7 @@ public final class PageRanges extends SetOfIntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PageRanges.class; } @@ -235,6 +237,7 @@ public final class PageRanges extends SetOfIntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "page-ranges"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PagesPerMinute.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PagesPerMinute.java index 184339fb978..4d05663d1db 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PagesPerMinute.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PagesPerMinute.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -78,6 +78,7 @@ public final class PagesPerMinute extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this pages per * minute attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof PagesPerMinute); @@ -93,6 +94,7 @@ public final class PagesPerMinute extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PagesPerMinute.class; } @@ -106,6 +108,7 @@ public final class PagesPerMinute extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "pages-per-minute"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PagesPerMinuteColor.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PagesPerMinuteColor.java index 32f15a4f165..0dc3d0c9420 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PagesPerMinuteColor.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PagesPerMinuteColor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -91,6 +91,7 @@ public final class PagesPerMinuteColor extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this pages per * minute color attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PagesPerMinuteColor); @@ -106,6 +107,7 @@ public final class PagesPerMinuteColor extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PagesPerMinuteColor.class; } @@ -119,6 +121,7 @@ public final class PagesPerMinuteColor extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "pages-per-minute-color"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PresentationDirection.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PresentationDirection.java index d4ff8581a2d..c40b6f3390e 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PresentationDirection.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PresentationDirection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -157,6 +157,7 @@ public final class PresentationDirection extends EnumSyntax /** * Returns the string table for class {@code PresentationDirection}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -165,6 +166,7 @@ public final class PresentationDirection extends EnumSyntax * Returns the enumeration value table for class * {@code PresentationDirection}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -179,6 +181,7 @@ public final class PresentationDirection extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PresentationDirection.class; } @@ -192,6 +195,7 @@ public final class PresentationDirection extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "presentation-direction"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrintQuality.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrintQuality.java index d00637fe23c..2e64522da7c 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrintQuality.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrintQuality.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -100,6 +100,7 @@ public class PrintQuality extends EnumSyntax /** * Returns the string table for class {@code PrintQuality}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); } @@ -107,6 +108,7 @@ public class PrintQuality extends EnumSyntax /** * Returns the enumeration value table for class {@code PrintQuality}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } @@ -114,6 +116,7 @@ public class PrintQuality extends EnumSyntax /** * Returns the lowest integer value used by class {@code PrintQuality}. */ + @Override protected int getOffset() { return 3; } @@ -128,6 +131,7 @@ public class PrintQuality extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrintQuality.class; } @@ -141,6 +145,7 @@ public class PrintQuality extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "print-quality"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterInfo.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterInfo.java index 4d8d2cf0601..39b8759d34d 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterInfo.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -87,6 +87,7 @@ public final class PrinterInfo extends TextSyntax * @return {@code true} if {@code object} is equivalent to this printer info * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PrinterInfo); } @@ -101,6 +102,7 @@ public final class PrinterInfo extends TextSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterInfo.class; } @@ -114,6 +116,7 @@ public final class PrinterInfo extends TextSyntax * * @return attribute category name */ + @Override public final String getName() { return "printer-info"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterIsAcceptingJobs.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterIsAcceptingJobs.java index 92e0d1fcfcd..f8a99d4db46 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterIsAcceptingJobs.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterIsAcceptingJobs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -100,6 +100,7 @@ public final class PrinterIsAcceptingJobs extends EnumSyntax /** * Returns the string table for class {@code PrinterIsAcceptingJobs}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -108,6 +109,7 @@ public final class PrinterIsAcceptingJobs extends EnumSyntax * Returns the enumeration value table for class * {@code PrinterIsAcceptingJobs}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -122,6 +124,7 @@ public final class PrinterIsAcceptingJobs extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterIsAcceptingJobs.class; } @@ -135,6 +138,7 @@ public final class PrinterIsAcceptingJobs extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "printer-is-accepting-jobs"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterLocation.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterLocation.java index a0cfed406ce..c23112c79d2 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterLocation.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterLocation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -83,6 +83,7 @@ public final class PrinterLocation extends TextSyntax * @return {@code true} if {@code object} is equivalent to this printer * location attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PrinterLocation); } @@ -97,6 +98,7 @@ public final class PrinterLocation extends TextSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterLocation.class; } @@ -110,6 +112,7 @@ public final class PrinterLocation extends TextSyntax * * @return attribute category name */ + @Override public final String getName() { return "printer-location"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMakeAndModel.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMakeAndModel.java index b5fe4a6ee95..eff34c8bd59 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMakeAndModel.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMakeAndModel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -82,6 +82,7 @@ public final class PrinterMakeAndModel extends TextSyntax * @return {@code true} if {@code object} is equivalent to this printer make * and model attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PrinterMakeAndModel); @@ -97,6 +98,7 @@ public final class PrinterMakeAndModel extends TextSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterMakeAndModel.class; } @@ -110,6 +112,7 @@ public final class PrinterMakeAndModel extends TextSyntax * * @return attribute category name */ + @Override public final String getName() { return "printer-make-and-model"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMessageFromOperator.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMessageFromOperator.java index 9ce0a098b80..e125d4955b3 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMessageFromOperator.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMessageFromOperator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -95,6 +95,7 @@ public final class PrinterMessageFromOperator extends TextSyntax * @return {@code true} if {@code object} is equivalent to this printer * message from operator attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PrinterMessageFromOperator); @@ -110,6 +111,7 @@ public final class PrinterMessageFromOperator extends TextSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterMessageFromOperator.class; } @@ -123,6 +125,7 @@ public final class PrinterMessageFromOperator extends TextSyntax * * @return attribute category name */ + @Override public final String getName() { return "printer-message-from-operator"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMoreInfo.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMoreInfo.java index 501ffdb6cfb..7e94507cf8c 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMoreInfo.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMoreInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -88,6 +88,7 @@ public final class PrinterMoreInfo extends URISyntax * @return {@code true} if {@code object} is equivalent to this printer more * info attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PrinterMoreInfo); @@ -103,6 +104,7 @@ public final class PrinterMoreInfo extends URISyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterMoreInfo.class; } @@ -116,6 +118,7 @@ public final class PrinterMoreInfo extends URISyntax * * @return attribute category name */ + @Override public final String getName() { return "printer-more-info"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMoreInfoManufacturer.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMoreInfoManufacturer.java index b6f8b92fc83..be6952fa00b 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMoreInfoManufacturer.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMoreInfoManufacturer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -88,6 +88,7 @@ public final class PrinterMoreInfoManufacturer extends URISyntax * @return {@code true} if {@code object} is equivalent to this printer more * info manufacturer attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PrinterMoreInfoManufacturer); @@ -103,6 +104,7 @@ public final class PrinterMoreInfoManufacturer extends URISyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterMoreInfoManufacturer.class; } @@ -116,6 +118,7 @@ public final class PrinterMoreInfoManufacturer extends URISyntax * * @return attribute category name */ + @Override public final String getName() { return "printer-more-info-manufacturer"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterName.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterName.java index 9a066c5d005..019ef3c8911 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterName.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -85,6 +85,7 @@ public final class PrinterName extends TextSyntax * @return {@code true} if {@code object} is equivalent to this printer name * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PrinterName); } @@ -99,6 +100,7 @@ public final class PrinterName extends TextSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterName.class; } @@ -112,6 +114,7 @@ public final class PrinterName extends TextSyntax * * @return attribute category name */ + @Override public final String getName() { return "printer-name"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterResolution.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterResolution.java index 3bba7feb751..a47d57209cd 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterResolution.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterResolution.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -108,6 +108,7 @@ public final class PrinterResolution extends ResolutionSyntax * @return {@code true} if {@code object} is equivalent to this printer * resolution attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof PrinterResolution); @@ -123,6 +124,7 @@ public final class PrinterResolution extends ResolutionSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterResolution.class; } @@ -136,6 +138,7 @@ public final class PrinterResolution extends ResolutionSyntax * * @return attribute category name */ + @Override public final String getName() { return "printer-resolution"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterState.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterState.java index 9cbb3da9473..f2c921360a4 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterState.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -114,6 +114,7 @@ implements PrintServiceAttribute { /** * Returns the string table for class {@code PrinterState}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -121,6 +122,7 @@ implements PrintServiceAttribute { /** * Returns the enumeration value table for class {@code PrinterState}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -135,6 +137,7 @@ implements PrintServiceAttribute { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterState.class; } @@ -148,6 +151,7 @@ implements PrintServiceAttribute { * * @return attribute category name */ + @Override public final String getName() { return "printer-state"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReason.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReason.java index 5c3b98ee127..8586713ed65 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReason.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReason.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -403,6 +403,7 @@ public class PrinterStateReason extends EnumSyntax implements Attribute { /** * Returns the string table for class {@code PrinterStateReason}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); } @@ -410,6 +411,7 @@ public class PrinterStateReason extends EnumSyntax implements Attribute { /** * Returns the enumeration value table for class {@code PrinterStateReason}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } @@ -424,6 +426,7 @@ public class PrinterStateReason extends EnumSyntax implements Attribute { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterStateReason.class; } @@ -437,6 +440,7 @@ public class PrinterStateReason extends EnumSyntax implements Attribute { * * @return attribute category name */ + @Override public final String getName() { return "printer-state-reason"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReasons.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReasons.java index 8e46b145651..fca59ca4bba 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReasons.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReasons.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -165,6 +165,7 @@ public final class PrinterStateReasons * {@link Severity Severity} * @since 1.5 */ + @Override public Severity put(PrinterStateReason reason, Severity severity) { if (reason == null) { throw new NullPointerException("reason is null"); @@ -185,6 +186,7 @@ public final class PrinterStateReasons * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterStateReasons.class; } @@ -198,6 +200,7 @@ public final class PrinterStateReasons * * @return attribute category name */ + @Override public final String getName() { return "printer-state-reasons"; } @@ -240,6 +243,7 @@ public final class PrinterStateReasons myEntrySet = entrySet; } + @Override public int size() { int result = 0; for (PrinterStateReason ignored : this) { @@ -248,6 +252,7 @@ public final class PrinterStateReasons return result; } + @Override public Iterator iterator() { return new PrinterStateReasonSetIterator(mySeverity, myEntrySet.iterator()); @@ -276,10 +281,12 @@ public final class PrinterStateReasons } } + @Override public boolean hasNext() { return myEntry != null; } + @Override public PrinterStateReason next() { if (myEntry == null) { throw new NoSuchElementException(); @@ -289,6 +296,7 @@ public final class PrinterStateReasons return result; } + @Override public void remove() { throw new UnsupportedOperationException(); } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterURI.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterURI.java index dfe0bc5cc1f..d6934aae172 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterURI.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterURI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -79,6 +79,7 @@ public final class PrinterURI extends URISyntax * @return {@code true} if {@code object} is equivalent to this * {@code PrinterURI} attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PrinterURI); } @@ -93,6 +94,7 @@ public final class PrinterURI extends URISyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterURI.class; } @@ -106,6 +108,7 @@ public final class PrinterURI extends URISyntax * * @return attribute category name */ + @Override public final String getName() { return "printer-uri"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/QueuedJobCount.java b/src/java.desktop/share/classes/javax/print/attribute/standard/QueuedJobCount.java index c56eeb35a83..e44ead9b7cf 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/QueuedJobCount.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/QueuedJobCount.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -76,6 +76,7 @@ public final class QueuedJobCount extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this queued job * count attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof QueuedJobCount); @@ -91,6 +92,7 @@ public final class QueuedJobCount extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return QueuedJobCount.class; } @@ -104,6 +106,7 @@ public final class QueuedJobCount extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "queued-job-count"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/ReferenceUriSchemesSupported.java b/src/java.desktop/share/classes/javax/print/attribute/standard/ReferenceUriSchemesSupported.java index 9784742030b..426fe4db3c4 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/ReferenceUriSchemesSupported.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/ReferenceUriSchemesSupported.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -154,6 +154,7 @@ public class ReferenceUriSchemesSupported /** * Returns the string table for class {@code ReferenceUriSchemesSupported}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); } @@ -162,6 +163,7 @@ public class ReferenceUriSchemesSupported * Returns the enumeration value table for class * {@code ReferenceUriSchemesSupported}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } @@ -177,6 +179,7 @@ public class ReferenceUriSchemesSupported * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return ReferenceUriSchemesSupported.class; } @@ -191,6 +194,7 @@ public class ReferenceUriSchemesSupported * * @return attribute category name */ + @Override public final String getName() { return "reference-uri-schemes-supported"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/RequestingUserName.java b/src/java.desktop/share/classes/javax/print/attribute/standard/RequestingUserName.java index 1d233eb7602..c2efb2da1d6 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/RequestingUserName.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/RequestingUserName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -92,6 +92,7 @@ public final class RequestingUserName extends TextSyntax * @return {@code true} if {@code object} is equivalent to this requesting * user name attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof RequestingUserName); @@ -107,6 +108,7 @@ public final class RequestingUserName extends TextSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return RequestingUserName.class; } @@ -120,6 +122,7 @@ public final class RequestingUserName extends TextSyntax * * @return attribute category name */ + @Override public final String getName() { return "requesting-user-name"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/Severity.java b/src/java.desktop/share/classes/javax/print/attribute/standard/Severity.java index 7543d4503a9..c217f12292f 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/Severity.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/Severity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -124,6 +124,7 @@ public final class Severity extends EnumSyntax implements Attribute { /** * Returns the string table for class {@code Severity}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -131,6 +132,7 @@ public final class Severity extends EnumSyntax implements Attribute { /** * Returns the enumeration value table for class {@code Severity}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -145,6 +147,7 @@ public final class Severity extends EnumSyntax implements Attribute { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return Severity.class; } @@ -157,6 +160,7 @@ public final class Severity extends EnumSyntax implements Attribute { * * @return attribute category name */ + @Override public final String getName() { return "severity"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/SheetCollate.java b/src/java.desktop/share/classes/javax/print/attribute/standard/SheetCollate.java index 45ec5f5374b..1754a27c86f 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/SheetCollate.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/SheetCollate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -191,6 +191,7 @@ public final class SheetCollate extends EnumSyntax /** * Returns the string table for class {@code SheetCollate}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -198,6 +199,7 @@ public final class SheetCollate extends EnumSyntax /** * Returns the enumeration value table for class {@code SheetCollate}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -212,6 +214,7 @@ public final class SheetCollate extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return SheetCollate.class; } @@ -225,6 +228,7 @@ public final class SheetCollate extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "sheet-collate"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/Sides.java b/src/java.desktop/share/classes/javax/print/attribute/standard/Sides.java index 86d421045b0..0321cd876de 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/Sides.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/Sides.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -183,6 +183,7 @@ public final class Sides extends EnumSyntax /** * Returns the string table for class {@code Sides}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -190,6 +191,7 @@ public final class Sides extends EnumSyntax /** * Returns the enumeration value table for class {@code Sides}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -203,6 +205,7 @@ public final class Sides extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return Sides.class; } @@ -215,6 +218,7 @@ public final class Sides extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "sides"; } diff --git a/src/java.desktop/share/classes/javax/swing/AbstractButton.java b/src/java.desktop/share/classes/javax/swing/AbstractButton.java index d4112b5ed04..a1961acce29 100644 --- a/src/java.desktop/share/classes/javax/swing/AbstractButton.java +++ b/src/java.desktop/share/classes/javax/swing/AbstractButton.java @@ -1070,13 +1070,13 @@ public abstract class AbstractButton extends JComponent implements ItemSelectabl Action oldValue = getAction(); if (action==null || !action.equals(a)) { action = a; - if (oldValue!=null) { + if (oldValue != null) { removeActionListener(oldValue); oldValue.removePropertyChangeListener(actionPropertyChangeListener); actionPropertyChangeListener = null; } configurePropertiesFromAction(action); - if (action!=null) { + if (action != null) { // Don't add if it is already a listener if (!isListener(ActionListener.class, action)) { addActionListener(action); diff --git a/src/java.desktop/share/classes/javax/swing/ActionPropertyChangeListener.java b/src/java.desktop/share/classes/javax/swing/ActionPropertyChangeListener.java index db12b6766b8..19b9152a1b5 100644 --- a/src/java.desktop/share/classes/javax/swing/ActionPropertyChangeListener.java +++ b/src/java.desktop/share/classes/javax/swing/ActionPropertyChangeListener.java @@ -111,7 +111,7 @@ abstract class ActionPropertyChangeListener while ((r = (OwnedWeakReference)queue.poll()) != null) { ActionPropertyChangeListener oldPCL = r.getOwner(); Action oldAction = oldPCL.getAction(); - if (oldAction!=null) { + if (oldAction != null) { oldAction.removePropertyChangeListener(oldPCL); } } diff --git a/src/java.desktop/share/classes/javax/swing/AncestorNotifier.java b/src/java.desktop/share/classes/javax/swing/AncestorNotifier.java index f3cfdd9d0ba..a3ed463d2d5 100644 --- a/src/java.desktop/share/classes/javax/swing/AncestorNotifier.java +++ b/src/java.desktop/share/classes/javax/swing/AncestorNotifier.java @@ -213,7 +213,7 @@ class AncestorNotifier implements ComponentListener, PropertyChangeListener, Ser public void propertyChange(PropertyChangeEvent evt) { String s = evt.getPropertyName(); - if (s!=null && (s.equals("parent") || s.equals("ancestor"))) { + if (s != null && (s.equals("parent") || s.equals("ancestor"))) { JComponent component = (JComponent)evt.getSource(); if (evt.getNewValue() != null) { diff --git a/src/java.desktop/share/classes/javax/swing/ArrayTable.java b/src/java.desktop/share/classes/javax/swing/ArrayTable.java index 7c38f9fb577..282bd3454ca 100644 --- a/src/java.desktop/share/classes/javax/swing/ArrayTable.java +++ b/src/java.desktop/share/classes/javax/swing/ArrayTable.java @@ -145,7 +145,7 @@ class ArrayTable implements Cloneable { */ public Object get(Object key) { Object value = null; - if (table !=null) { + if (table != null) { if (isArray()) { Object[] array = (Object[])table; for (int i = 0; i extends JComponent implements Scrollable, Accessible } Rectangle newFirstRect = getCellBounds(newFirst,newFirst); Rectangle firstRect = getCellBounds(first,first); - if ((newFirstRect != null) && (firstRect!=null)) { + if ((newFirstRect != null) && (firstRect != null)) { while ( (newFirstRect.y + visibleRect.height < firstRect.y + firstRect.height) && (newFirstRect.y < firstRect.y) ) { diff --git a/src/java.desktop/share/classes/javax/swing/JOptionPane.java b/src/java.desktop/share/classes/javax/swing/JOptionPane.java index 707b9febf10..c41400b9b37 100644 --- a/src/java.desktop/share/classes/javax/swing/JOptionPane.java +++ b/src/java.desktop/share/classes/javax/swing/JOptionPane.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -1656,7 +1656,7 @@ public class JOptionPane extends JComponent implements Accessible return getDesktopPaneForComponent(parentComponent.getParent()); } - private static final Object sharedFrameKey = JOptionPane.class; + private static volatile Frame sharedRootFrame; /** * Sets the frame to use for class methods in which a frame is @@ -1668,11 +1668,7 @@ public class JOptionPane extends JComponent implements Accessible * @param newRootFrame the default Frame to use */ public static void setRootFrame(Frame newRootFrame) { - if (newRootFrame != null) { - SwingUtilities.appContextPut(sharedFrameKey, newRootFrame); - } else { - SwingUtilities.appContextRemove(sharedFrameKey); - } + sharedRootFrame = newRootFrame; } /** @@ -1687,13 +1683,10 @@ public class JOptionPane extends JComponent implements Accessible * @see java.awt.GraphicsEnvironment#isHeadless */ public static Frame getRootFrame() throws HeadlessException { - Frame sharedFrame = - (Frame)SwingUtilities.appContextGet(sharedFrameKey); - if (sharedFrame == null) { - sharedFrame = SwingUtilities.getSharedOwnerFrame(); - SwingUtilities.appContextPut(sharedFrameKey, sharedFrame); + if (sharedRootFrame == null) { + sharedRootFrame = SwingUtilities.getSharedOwnerFrame(); } - return sharedFrame; + return sharedRootFrame; } /** diff --git a/src/java.desktop/share/classes/javax/swing/JPopupMenu.java b/src/java.desktop/share/classes/javax/swing/JPopupMenu.java index 2ecc7cf3b1e..f3d11a29b4a 100644 --- a/src/java.desktop/share/classes/javax/swing/JPopupMenu.java +++ b/src/java.desktop/share/classes/javax/swing/JPopupMenu.java @@ -975,7 +975,7 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { if (newFrame != frame) { // Use the invoker's frame so that events // are propagated properly - if (newFrame!=null) { + if (newFrame != null) { this.frame = newFrame; if(popup != null) { setVisible(false); @@ -1012,7 +1012,7 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { */ JPopupMenu getRootPopupMenu() { JPopupMenu mp = this; - while((mp!=null) && (mp.isPopupMenu()!=true) && + while((mp != null) && (mp.isPopupMenu()!=true) && (mp.getInvoker() != null) && (mp.getInvoker().getParent() instanceof JPopupMenu popupMenu) ) { @@ -1182,7 +1182,7 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { private static Frame getFrame(Component c) { Component w = c; - while(!(w instanceof Frame) && (w!=null)) { + while(!(w instanceof Frame) && (w != null)) { w = w.getParent(); } return (Frame)w; diff --git a/src/java.desktop/share/classes/javax/swing/JTextField.java b/src/java.desktop/share/classes/javax/swing/JTextField.java index 0d66209d8af..b6d8639c994 100644 --- a/src/java.desktop/share/classes/javax/swing/JTextField.java +++ b/src/java.desktop/share/classes/javax/swing/JTextField.java @@ -581,13 +581,13 @@ public class JTextField extends JTextComponent implements SwingConstants { Action oldValue = getAction(); if (action==null || !action.equals(a)) { action = a; - if (oldValue!=null) { + if (oldValue != null) { removeActionListener(oldValue); oldValue.removePropertyChangeListener(actionPropertyChangeListener); actionPropertyChangeListener = null; } configurePropertiesFromAction(action); - if (action!=null) { + if (action != null) { // Don't add if it is already a listener if (!isListener(ActionListener.class, action)) { addActionListener(action); diff --git a/src/java.desktop/share/classes/javax/swing/JTree.java b/src/java.desktop/share/classes/javax/swing/JTree.java index 622c55e2ede..397584b6e4a 100644 --- a/src/java.desktop/share/classes/javax/swing/JTree.java +++ b/src/java.desktop/share/classes/javax/swing/JTree.java @@ -2087,7 +2087,7 @@ public class JTree extends JComponent implements Scrollable, Accessible value = expandedState.get(path); if (value == null || !value) return false; - } while( (path=path.getParentPath())!=null ); + } while( (path=path.getParentPath()) != null ); return true; } diff --git a/src/java.desktop/share/classes/javax/swing/KeyboardManager.java b/src/java.desktop/share/classes/javax/swing/KeyboardManager.java index 42a6403ddb5..8206ab5cea6 100644 --- a/src/java.desktop/share/classes/javax/swing/KeyboardManager.java +++ b/src/java.desktop/share/classes/javax/swing/KeyboardManager.java @@ -330,7 +330,7 @@ class KeyboardManager { return; } Hashtable keyMap = containerMap.get(topContainer); - if (keyMap!=null) { + if (keyMap != null) { Vector v = (Vector)keyMap.get(JMenuBar.class); if (v != null) { v.removeElement(mb); diff --git a/src/java.desktop/share/classes/javax/swing/PopupFactory.java b/src/java.desktop/share/classes/javax/swing/PopupFactory.java index 208177fc55e..7245820c289 100644 --- a/src/java.desktop/share/classes/javax/swing/PopupFactory.java +++ b/src/java.desktop/share/classes/javax/swing/PopupFactory.java @@ -925,7 +925,7 @@ public class PopupFactory { add to that, otherwise add to the window. */ while (!(parent instanceof Window) && - (parent!=null)) { + (parent != null)) { parent = parent.getParent(); } diff --git a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java index afe1c444c31..ae5faf64f3b 100644 --- a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java +++ b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java @@ -506,7 +506,7 @@ public class SwingUtilities implements SwingConstants public static boolean isDescendingFrom(Component a,Component b) { if(a == b) return true; - for(Container p = a.getParent();p!=null;p=p.getParent()) + for(Container p = a.getParent(); p != null; p = p.getParent()) if(p == b) return true; return false; diff --git a/src/java.desktop/share/classes/javax/swing/TimerQueue.java b/src/java.desktop/share/classes/javax/swing/TimerQueue.java index 4ef2769652a..39c06e57479 100644 --- a/src/java.desktop/share/classes/javax/swing/TimerQueue.java +++ b/src/java.desktop/share/classes/javax/swing/TimerQueue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -28,7 +28,7 @@ package javax.swing; import java.util.concurrent.*; import java.util.concurrent.locks.*; import java.util.concurrent.atomic.AtomicLong; -import sun.awt.AppContext; +import sun.awt.util.ThreadGroupUtils; /** * Internal class to manage all Timers using one thread. @@ -40,8 +40,7 @@ import sun.awt.AppContext; */ class TimerQueue implements Runnable { - private static final Object sharedInstanceKey = - new StringBuffer("TimerQueue.sharedInstanceKey"); + private static volatile TimerQueue sharedInstance; private final DelayQueue queue; private volatile boolean running; @@ -69,14 +68,10 @@ class TimerQueue implements Runnable public static TimerQueue sharedInstance() { synchronized (classLock) { - TimerQueue sharedInst = (TimerQueue) - SwingUtilities.appContextGet( - sharedInstanceKey); - if (sharedInst == null) { - sharedInst = new TimerQueue(); - SwingUtilities.appContextPut(sharedInstanceKey, sharedInst); + if (sharedInstance == null) { + sharedInstance = new TimerQueue(); } - return sharedInst; + return sharedInstance; } } @@ -88,9 +83,10 @@ class TimerQueue implements Runnable return; } try { - final ThreadGroup threadGroup = AppContext.getAppContext().getThreadGroup(); + final ThreadGroup threadGroup = ThreadGroupUtils.getRootThreadGroup(); String name = "TimerQueue"; Thread timerThread = new Thread(threadGroup, this, name, 0, false); + timerThread.setContextClassLoader(null); timerThread.setDaemon(true); timerThread.setPriority(Thread.NORM_PRIORITY); timerThread.start(); @@ -183,11 +179,6 @@ class TimerQueue implements Runnable timer.getLock().unlock(); } } catch (InterruptedException ie) { - // Shouldn't ignore InterruptedExceptions here, so AppContext - // is disposed gracefully, see 6799345 for details - if (AppContext.getAppContext().isDisposed()) { - break; - } } } } finally { diff --git a/src/java.desktop/share/classes/javax/swing/TransferHandler.java b/src/java.desktop/share/classes/javax/swing/TransferHandler.java index f412314b28f..0f2229482bc 100644 --- a/src/java.desktop/share/classes/javax/swing/TransferHandler.java +++ b/src/java.desktop/share/classes/javax/swing/TransferHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -38,7 +38,6 @@ import javax.swing.text.JTextComponent; import sun.reflect.misc.MethodUtil; import sun.swing.SwingUtilities2; -import sun.awt.AppContext; import sun.swing.*; import sun.awt.SunToolkit; @@ -1096,15 +1095,12 @@ public class TransferHandler implements Serializable { private String propertyName; private static SwingDragGestureRecognizer recognizer = null; + private static DropHandler handler; private static DropTargetListener getDropTargetListener() { synchronized(DropHandler.class) { - DropHandler handler = - (DropHandler)AppContext.getAppContext().get(DropHandler.class); - if (handler == null) { handler = new DropHandler(); - AppContext.getAppContext().put(DropHandler.class, handler); } return handler; @@ -1725,29 +1721,22 @@ public class TransferHandler implements Serializable { } } + private static Clipboard clipboard; /** * Returns the clipboard to use for cut/copy/paste. */ private Clipboard getClipboard(JComponent c) { - if (SwingUtilities2.canAccessSystemClipboard()) { + if (!GraphicsEnvironment.isHeadless()) { return c.getToolkit().getSystemClipboard(); } - Clipboard clipboard = (Clipboard)sun.awt.AppContext.getAppContext(). - get(SandboxClipboardKey); - if (clipboard == null) { - clipboard = new Clipboard("Sandboxed Component Clipboard"); - sun.awt.AppContext.getAppContext().put(SandboxClipboardKey, - clipboard); + // Likely it is impossible to be here in headless. + synchronized (TransferHandler.class) { + if (clipboard == null) { + clipboard = new Clipboard("Headless clipboard"); + } + return clipboard; } - return clipboard; } - - /** - * Key used in app context to lookup Clipboard to use if access to - * System clipboard is denied. - */ - private static Object SandboxClipboardKey = new Object(); - } } diff --git a/src/java.desktop/share/classes/javax/swing/UIDefaults.java b/src/java.desktop/share/classes/javax/swing/UIDefaults.java index d59a46dba32..67a739360f9 100644 --- a/src/java.desktop/share/classes/javax/swing/UIDefaults.java +++ b/src/java.desktop/share/classes/javax/swing/UIDefaults.java @@ -1169,7 +1169,7 @@ public class UIDefaults extends Hashtable */ private Class[] getClassArray(Object[] args) { Class[] types = null; - if (args!=null) { + if (args != null) { types = new Class[args.length]; for (int i = 0; i< args.length; i++) { /* PENDING(ges): At present only the primitive types @@ -1199,7 +1199,7 @@ public class UIDefaults extends Hashtable private String printArgs(Object[] array) { String s = "{"; - if (array !=null) { + if (array != null) { for (int i = 0 ; i < array.length-1; i++) { s = s.concat(array[i] + ","); } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboPopup.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboPopup.java index 38798d4c94f..42791772c2d 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboPopup.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboPopup.java @@ -1070,11 +1070,11 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { ComponentOrientation o =(ComponentOrientation)e.getNewValue(); JList list = getList(); - if (list!=null && list.getComponentOrientation()!=o) { + if (list != null && list.getComponentOrientation()!=o) { list.setComponentOrientation(o); } - if (scroller!=null && scroller.getComponentOrientation()!=o) { + if (scroller != null && scroller.getComponentOrientation()!=o) { scroller.setComponentOrientation(o); } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java index 0a4aa03dce9..ab5fdb12c5a 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java @@ -876,7 +876,7 @@ public class BasicListUI extends ListUI } Long l = (Long)UIManager.get("List.timeFactor"); - timeFactor = (l!=null) ? l.longValue() : 1000L; + timeFactor = (l != null) ? l.longValue() : 1000L; updateIsFileList(); } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuBarUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuBarUI.java index 2079aafd3a2..b7310b56aef 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuBarUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuBarUI.java @@ -125,7 +125,7 @@ public class BasicMenuBarUI extends MenuBarUI { for (int i = 0; i < menuBar.getMenuCount(); i++) { JMenu menu = menuBar.getMenu(i); - if (menu!=null) + if (menu != null) menu.getModel().addChangeListener(changeListener); } menuBar.addContainerListener(containerListener); @@ -167,7 +167,7 @@ public class BasicMenuBarUI extends MenuBarUI { * Uninstalls default properties. */ protected void uninstallDefaults() { - if (menuBar!=null) { + if (menuBar != null) { LookAndFeel.uninstallBorder(menuBar); } } @@ -180,7 +180,7 @@ public class BasicMenuBarUI extends MenuBarUI { for (int i = 0; i < menuBar.getMenuCount(); i++) { JMenu menu = menuBar.getMenu(i); - if (menu !=null) + if (menu != null) menu.getModel().removeChangeListener(changeListener); } @@ -240,7 +240,7 @@ public class BasicMenuBarUI extends MenuBarUI { int i,c; for(i=0,c = menuBar.getMenuCount() ; i < c ; i++) { JMenu menu = menuBar.getMenu(i); - if(menu !=null && menu.isSelected()) { + if(menu != null && menu.isSelected()) { menuBar.getSelectionModel().setSelectedIndex(i); break; } @@ -277,7 +277,7 @@ public class BasicMenuBarUI extends MenuBarUI { MenuElement[] me; MenuElement[] subElements; JMenu menu = menuBar.getMenu(0); - if (menu!=null) { + if (menu != null) { me = new MenuElement[3]; me[0] = (MenuElement) menuBar; me[1] = (MenuElement) menu; diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java index 8df27d3e3cb..b72a2d8a140 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java @@ -908,7 +908,7 @@ public class BasicPopupMenuUI extends PopupMenuUI { } boolean isInPopup(Component src) { - for (Component c=src; c!=null; c=c.getParent()) { + for (Component c=src; c != null; c=c.getParent()) { if (c instanceof Window) { break; } else if (c instanceof JPopupMenu) { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSpinnerUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSpinnerUI.java index 2b871343697..7080fa3aac1 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSpinnerUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSpinnerUI.java @@ -688,7 +688,7 @@ public class BasicSpinnerUI extends SpinnerUI arrowButton = (JButton)e.getSource(); } } else { - if (arrowButton!=null && !arrowButton.getModel().isPressed() + if (arrowButton != null && !arrowButton.getModel().isPressed() && autoRepeatTimer.isRunning()) { autoRepeatTimer.stop(); spinner = null; diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSplitPaneUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSplitPaneUI.java index 270181f4600..1d8c471e9ec 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSplitPaneUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSplitPaneUI.java @@ -2273,7 +2273,7 @@ public class BasicSplitPaneUI extends SplitPaneUI JSplitPane parentSplitPane = (JSplitPane)SwingUtilities.getAncestorOfClass( JSplitPane.class, splitPane); - if (parentSplitPane!=null) { + if (parentSplitPane != null) { parentSplitPane.requestFocus(); } } @@ -2307,7 +2307,7 @@ public class BasicSplitPaneUI extends SplitPaneUI } while (splitPane.isAncestorOf(focusOn) && !focusFrom.contains(focusOn)); } - if ( focusOn!=null && !splitPane.isAncestorOf(focusOn) ) { + if ( focusOn != null && !splitPane.isAncestorOf(focusOn) ) { focusOn.requestFocus(); } } @@ -2323,7 +2323,7 @@ public class BasicSplitPaneUI extends SplitPaneUI if (focusOn != null) { // don't change the focus if the new focused component belongs // to the same splitpane and the same side - if ( focus!=null && + if ( focus != null && ( (SwingUtilities.isDescendingFrom(focus, left) && SwingUtilities.isDescendingFrom(focusOn, left)) || (SwingUtilities.isDescendingFrom(focus, right) && @@ -2338,15 +2338,15 @@ public class BasicSplitPaneUI extends SplitPaneUI Component left = splitPane.getLeftComponent(); Component right = splitPane.getRightComponent(); Component next; - if (focus!=null && SwingUtilities.isDescendingFrom(focus, left) && - right!=null) { + if (focus != null && SwingUtilities.isDescendingFrom(focus, left) && + right != null) { next = getFirstAvailableComponent(right); if (next != null) { return next; } } JSplitPane parentSplitPane = (JSplitPane)SwingUtilities.getAncestorOfClass(JSplitPane.class, splitPane); - if (parentSplitPane!=null) { + if (parentSplitPane != null) { // focus next side of the parent split pane next = getNextSide(parentSplitPane, focus); } else { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java index 842e8892c76..6e0d0101b9c 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java @@ -528,7 +528,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { } tabPane.removeContainerListener(getHandler()); - if (htmlViews!=null) { + if (htmlViews != null) { htmlViews.removeAllElements(); htmlViews = null; } @@ -4090,7 +4090,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { setHtmlView(v, inserted, index); } } else { // Not HTML - if (htmlViews!=null) { // Add placeholder + if (htmlViews != null) { // Add placeholder setHtmlView(null, inserted, index); } // else nada! } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToolBarUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToolBarUI.java index 41ab6137976..1bcd4a9be8d 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToolBarUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToolBarUI.java @@ -980,7 +980,7 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants { toolBar.setOrientation( orientation ); - if (dragWindow !=null) + if (dragWindow != null) dragWindow.setOrientation(orientation); } @@ -1616,7 +1616,7 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants this.orientation = o; Dimension size = getSize(); setSize(new Dimension(size.height, size.width)); - if (offset!=null) { + if (offset != null) { if( BasicGraphicsUtils.isLeftToRight(toolBar) ) { setOffset(new Point(offset.y, offset.x)); } else if( o == JToolBar.HORIZONTAL ) { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTreeUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTreeUI.java index 28beaee6929..096fe7cc5f7 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTreeUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTreeUI.java @@ -943,7 +943,7 @@ public class BasicTreeUI extends TreeUI lineTypeDashed = UIManager.getBoolean("Tree.lineTypeDashed"); Long l = (Long)UIManager.get("Tree.timeFactor"); - timeFactor = (l!=null) ? l.longValue() : 1000L; + timeFactor = (l != null) ? l.longValue() : 1000L; Object showsRootHandles = UIManager.get("Tree.showsRootHandles"); if (showsRootHandles != null) { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/DragRecognitionSupport.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/DragRecognitionSupport.java index abdd460e44b..517dad35d45 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/DragRecognitionSupport.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/DragRecognitionSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -29,14 +29,12 @@ import java.awt.event.*; import java.awt.dnd.DragSource; import javax.swing.*; import sun.awt.dnd.SunDragSourceContextPeer; -import sun.awt.AppContext; /** * Drag gesture recognition support for classes that have a * TransferHandler. The gesture for a drag in this class is a mouse * press followed by movement by DragSource.getDragThreshold() - * pixels. An instance of this class is maintained per AppContext, and the - * public static methods call into the appropriate instance. + * pixels. * * @author Shannon Hickey */ @@ -53,19 +51,14 @@ class DragRecognitionSupport { public void dragStarting(MouseEvent me); } + private static DragRecognitionSupport support; /** - * Returns the DragRecognitionSupport for the caller's AppContext. + * Returns the DragRecognitionSupport instance. */ - private static DragRecognitionSupport getDragRecognitionSupport() { - DragRecognitionSupport support = - (DragRecognitionSupport)AppContext.getAppContext(). - get(DragRecognitionSupport.class); - + private static synchronized DragRecognitionSupport getDragRecognitionSupport() { if (support == null) { support = new DragRecognitionSupport(); - AppContext.getAppContext().put(DragRecognitionSupport.class, support); } - return support; } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java index 1bdff19fa2d..cfcc014940a 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java @@ -660,7 +660,7 @@ public abstract class AbstractRegionPainter implements Painter { ImageScalingHelper.paint(g, 0, 0, w, h, img, insets, dstInsets, ImageScalingHelper.PaintType.PAINT9_STRETCH, ImageScalingHelper.PAINT_ALL); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - oldScalingHints!=null?oldScalingHints:RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + oldScalingHints != null ? oldScalingHints:RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); } else { // render directly paint0(g, c, w, h, extendedCacheKeys); diff --git a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java index c9340a62368..05fa3bbc9b6 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java @@ -531,7 +531,7 @@ public class NimbusLookAndFeel extends SynthLookAndFeel { public Object createValue(UIDefaults table) { Object obj = null; // check specified state - if (state!=null){ + if (state != null){ obj = uiDefaults.get(prefix+"["+state+"]."+suffix); } // check enabled state diff --git a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/SynthPainterImpl.java b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/SynthPainterImpl.java index ca19a74b6ac..1842073588b 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/SynthPainterImpl.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/SynthPainterImpl.java @@ -63,13 +63,13 @@ class SynthPainterImpl extends SynthPainter { if (p != null) { if (g instanceof Graphics2D){ Graphics2D gfx = (Graphics2D)g; - if (transform!=null){ + if (transform != null) { gfx.transform(transform); } gfx.translate(x, y); p.paint(gfx, ctx.getComponent(), w, h); gfx.translate(-x, -y); - if (transform!=null){ + if (transform != null){ try { gfx.transform(transform.createInverse()); } catch (NoninvertibleTransformException e) { @@ -85,7 +85,7 @@ class SynthPainterImpl extends SynthPainter { BufferedImage img = new BufferedImage(w,h, BufferedImage.TYPE_INT_ARGB); Graphics2D gfx = img.createGraphics(); - if (transform!=null){ + if (transform != null){ gfx.transform(transform); } p.paint(gfx, ctx.getComponent(), w, h); diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java index 77ca2a1fc05..ad10e70a837 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java @@ -814,7 +814,7 @@ public class SynthComboBoxUI extends BasicComboBoxUI implements public void propertyChange(PropertyChangeEvent evt) { ComboBoxEditor newEditor = comboBox.getEditor(); if (editor != newEditor){ - if (editorComponent!=null){ + if (editorComponent != null) { editorComponent.removeFocusListener(this); } editor = newEditor; diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthScrollPaneUI.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthScrollPaneUI.java index 96541d51f67..0c3a17fbdbb 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthScrollPaneUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthScrollPaneUI.java @@ -225,7 +225,7 @@ public class SynthScrollPaneUI extends BasicScrollPaneUI private int getComponentState(JComponent c) { int baseState = SynthLookAndFeel.getComponentState(c); - if (viewportViewFocusHandler!=null && viewportViewHasFocus){ + if (viewportViewFocusHandler != null && viewportViewHasFocus) { baseState = baseState | FOCUSED; } return baseState; diff --git a/src/java.desktop/share/classes/javax/swing/text/JTextComponent.java b/src/java.desktop/share/classes/javax/swing/text/JTextComponent.java index 59cee1e12ee..f81ba9d66c2 100644 --- a/src/java.desktop/share/classes/javax/swing/text/JTextComponent.java +++ b/src/java.desktop/share/classes/javax/swing/text/JTextComponent.java @@ -1181,7 +1181,7 @@ public abstract class JTextComponent extends JComponent implements Scrollable, A Hashtable h = new Hashtable(); for (Action a : actions) { String value = (String)a.getValue(Action.NAME); - h.put((value!=null ? value:""), a); + h.put((value != null ? value : ""), a); } for (KeyBinding binding : bindings) { Action a = h.get(binding.actionName); diff --git a/src/java.desktop/share/classes/javax/swing/text/TextAction.java b/src/java.desktop/share/classes/javax/swing/text/TextAction.java index 5c11c994ab2..d05f7e24a6b 100644 --- a/src/java.desktop/share/classes/javax/swing/text/TextAction.java +++ b/src/java.desktop/share/classes/javax/swing/text/TextAction.java @@ -107,11 +107,11 @@ public abstract class TextAction extends AbstractAction { Hashtable h = new Hashtable(); for (Action a : list1) { String value = (String)a.getValue(Action.NAME); - h.put((value!=null ? value:""), a); + h.put((value != null ? value : ""), a); } for (Action a : list2) { String value = (String)a.getValue(Action.NAME); - h.put((value!=null ? value:""), a); + h.put((value != null ? value : ""), a); } Action[] actions = new Action[h.size()]; int index = 0; diff --git a/src/java.desktop/share/classes/javax/swing/tree/DefaultTreeCellEditor.java b/src/java.desktop/share/classes/javax/swing/tree/DefaultTreeCellEditor.java index 4662c95c01f..3f3721dd30a 100644 --- a/src/java.desktop/share/classes/javax/swing/tree/DefaultTreeCellEditor.java +++ b/src/java.desktop/share/classes/javax/swing/tree/DefaultTreeCellEditor.java @@ -259,7 +259,7 @@ public class DefaultTreeCellEditor implements ActionListener, TreeCellEditor, ((MouseEvent)event).getY()); editable = (lastPath != null && path != null && lastPath.equals(path)); - if (path!=null) { + if (path != null) { lastRow = tree.getRowForPath(path); Object value = path.getLastPathComponent(); boolean isSelected = tree.isRowSelected(lastRow); diff --git a/src/java.desktop/share/classes/sun/awt/datatransfer/DesktopDatatransferServiceImpl.java b/src/java.desktop/share/classes/sun/awt/datatransfer/DesktopDatatransferServiceImpl.java index e96ca888ee3..4234dbad59a 100644 --- a/src/java.desktop/share/classes/sun/awt/datatransfer/DesktopDatatransferServiceImpl.java +++ b/src/java.desktop/share/classes/sun/awt/datatransfer/DesktopDatatransferServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -25,7 +25,6 @@ package sun.awt.datatransfer; -import sun.awt.AppContext; import sun.datatransfer.DesktopDatatransferService; import java.awt.EventQueue; @@ -43,8 +42,6 @@ import java.util.function.Supplier; */ public class DesktopDatatransferServiceImpl implements DesktopDatatransferService { - private static final Object FLAVOR_MAP_KEY = new Object(); - @Override public void invokeOnEventThread(Runnable r) { EventQueue.invokeLater(r); @@ -59,13 +56,11 @@ public class DesktopDatatransferServiceImpl implements DesktopDatatransferServic return null; } + private FlavorMap fm; @Override - public FlavorMap getFlavorMap(Supplier supplier) { - AppContext context = AppContext.getAppContext(); - FlavorMap fm = (FlavorMap) context.get(FLAVOR_MAP_KEY); + public synchronized FlavorMap getFlavorMap(Supplier supplier) { if (fm == null) { fm = supplier.get(); - context.put(FLAVOR_MAP_KEY, fm); } return fm; } diff --git a/src/java.desktop/share/classes/sun/awt/datatransfer/SunClipboard.java b/src/java.desktop/share/classes/sun/awt/datatransfer/SunClipboard.java index bc8071a798b..4ccee481a9e 100644 --- a/src/java.desktop/share/classes/sun/awt/datatransfer/SunClipboard.java +++ b/src/java.desktop/share/classes/sun/awt/datatransfer/SunClipboard.java @@ -226,9 +226,6 @@ public abstract class SunClipboard extends Clipboard { * argument is not {@code null} and is not equal to the current * contents context. * - * @param disposedContext the AppContext that is disposed or - * {@code null} if the ownership is lost because another - * application acquired ownership. */ protected void lostOwnershipLater() { SunToolkit.postEvent(new PeerEvent(this, () -> lostOwnershipNow(), diff --git a/src/java.desktop/share/classes/sun/awt/dnd/SunDragSourceContextPeer.java b/src/java.desktop/share/classes/sun/awt/dnd/SunDragSourceContextPeer.java index 258cebd6616..4aad70961bc 100644 --- a/src/java.desktop/share/classes/sun/awt/dnd/SunDragSourceContextPeer.java +++ b/src/java.desktop/share/classes/sun/awt/dnd/SunDragSourceContextPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -264,8 +264,7 @@ public abstract class SunDragSourceContextPeer implements DragSourceContextPeer modifiers, x, y); EventDispatcher dispatcher = new EventDispatcher(dispatchType, event); - SunToolkit.invokeLaterOnAppContext( - SunToolkit.targetToAppContext(getComponent()), dispatcher); + SunToolkit.invokeLater(dispatcher); startSecondaryEventLoop(); } @@ -310,8 +309,7 @@ public abstract class SunDragSourceContextPeer implements DragSourceContextPeer EventDispatcher dispatcher = new EventDispatcher(DISPATCH_EXIT, event); - SunToolkit.invokeLaterOnAppContext( - SunToolkit.targetToAppContext(getComponent()), dispatcher); + SunToolkit.invokeLater(dispatcher); startSecondaryEventLoop(); } @@ -341,8 +339,7 @@ public abstract class SunDragSourceContextPeer implements DragSourceContextPeer EventDispatcher dispatcher = new EventDispatcher(DISPATCH_FINISH, event); - SunToolkit.invokeLaterOnAppContext( - SunToolkit.targetToAppContext(getComponent()), dispatcher); + SunToolkit.invokeLater(dispatcher); startSecondaryEventLoop(); setNativeContext(0); diff --git a/src/java.desktop/share/classes/sun/awt/dnd/SunDropTargetContextPeer.java b/src/java.desktop/share/classes/sun/awt/dnd/SunDropTargetContextPeer.java index 686c3166441..70d1bda9cd0 100644 --- a/src/java.desktop/share/classes/sun/awt/dnd/SunDropTargetContextPeer.java +++ b/src/java.desktop/share/classes/sun/awt/dnd/SunDropTargetContextPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -55,7 +55,6 @@ import sun.util.logging.PlatformLogger; import java.io.IOException; import java.io.InputStream; -import sun.awt.AppContext; import sun.awt.SunToolkit; import sun.awt.datatransfer.DataTransferer; import sun.awt.datatransfer.ToolkitThreadBlockedHandler; @@ -558,7 +557,6 @@ public abstract class SunDropTargetContextPeer implements DropTargetContextPeer, final long nativeCtxt, final int eventID, final boolean dispatchType) { - AppContext appContext = SunToolkit.targetToAppContext(component); EventDispatcher dispatcher = new EventDispatcher(this, dropAction, actions, formats, nativeCtxt, @@ -572,7 +570,7 @@ public abstract class SunDropTargetContextPeer implements DropTargetContextPeer, } // schedule callback - SunToolkit.postEvent(appContext, event); + SunToolkit.postEvent(event); eventPosted(event); diff --git a/src/java.desktop/share/classes/sun/awt/im/ExecutableInputMethodManager.java b/src/java.desktop/share/classes/sun/awt/im/ExecutableInputMethodManager.java index d8246a4947c..f67f560e004 100644 --- a/src/java.desktop/share/classes/sun/awt/im/ExecutableInputMethodManager.java +++ b/src/java.desktop/share/classes/sun/awt/im/ExecutableInputMethodManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -35,7 +35,6 @@ import java.awt.PopupMenu; import java.awt.Menu; import java.awt.MenuItem; import java.awt.Toolkit; -import sun.awt.AppContext; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InvocationEvent; @@ -177,9 +176,8 @@ class ExecutableInputMethodManager extends InputMethodManager lock, true); - AppContext requesterAppContext = SunToolkit.targetToAppContext(requester); synchronized (lock) { - SunToolkit.postEvent(requesterAppContext, event); + SunToolkit.postEvent(event); while (!event.isDispatched()) { lock.wait(); } diff --git a/src/java.desktop/share/classes/sun/font/HBShaper.java b/src/java.desktop/share/classes/sun/font/HBShaper.java index 7d3f58fb88f..3a532072004 100644 --- a/src/java.desktop/share/classes/sun/font/HBShaper.java +++ b/src/java.desktop/share/classes/sun/font/HBShaper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -138,6 +138,7 @@ public class HBShaper { private static final MemorySegment get_h_advance_stub; private static final MemorySegment get_v_advance_stub; private static final MemorySegment get_contour_pt_stub; + private static final MemorySegment get_table_data_fn_stub; private static final MemorySegment store_layout_results_stub; @@ -209,6 +210,12 @@ public class HBShaper { jdk_hb_shape_handle = tmp4; Arena garena = Arena.global(); // creating stubs that exist until VM exit. + + get_table_data_fn_stub = getUpcallStub(garena, + "getFontTableData", // method name + JAVA_INT, // return type + JAVA_INT, ADDRESS); // arg types + FunctionDescriptor get_var_glyph_fd = getFunctionDescriptor(JAVA_INT, // return type ADDRESS, ADDRESS, JAVA_INT, JAVA_INT, ADDRESS, ADDRESS); // arg types MethodHandle get_var_glyph_mh = @@ -303,15 +310,9 @@ public class HBShaper { clusterHandle = getVarHandle(GlyphInfoLayout, "cluster"); } - - /* - * This is expensive but it is done just once per font. - * The unbound stub could be cached but the savings would - * be very low in the only case it is used. - */ @SuppressWarnings("restricted") - private static MemorySegment getBoundUpcallStub - (Arena arena, Class clazz, Object bindArg, String mName, + private static MemorySegment getUpcallStub + (Arena arena, String mName, MemoryLayout retType, MemoryLayout... argTypes) { try { @@ -320,10 +321,8 @@ public class HBShaper { FunctionDescriptor.ofVoid(argTypes) : FunctionDescriptor.of(retType, argTypes); MethodType mType = nativeDescriptor.toMethodType(); - mType = mType.insertParameterTypes(0, clazz); MethodHandle mh = MH_LOOKUP.findStatic(HBShaper.class, mName, mType); - MethodHandle bound_handle = mh.bindTo(bindArg); - return LINKER.upcallStub(bound_handle, nativeDescriptor, arena); + return LINKER.upcallStub(mh, nativeDescriptor, arena); } catch (IllegalAccessException | NoSuchMethodException e) { return null; } @@ -480,15 +479,16 @@ public class HBShaper { }); } - private static int getFontTableData(Font2D font2D, - int tag, - MemorySegment data_ptr_out) { + private static int getFontTableData(int tag, MemorySegment data_ptr_out) { /* * On return, the data_out_ptr will point to memory allocated by native malloc, * so it will be freed by the caller using native free - when it is * done with it. */ + + Font2D font2D = scopedVars.get().font(); + @SuppressWarnings("restricted") MemorySegment data_ptr = data_ptr_out.reinterpret(ADDRESS.byteSize()); if (tag == 0) { @@ -539,10 +539,6 @@ public class HBShaper { private static class FaceRef implements DisposerRecord { private Font2D font2D; private MemorySegment face; - // get_table_data_fn uses an Arena managed by GC, - // so we need to keep a reference to it here until - // this FaceRef is collected. - private MemorySegment get_table_data_fn; private FaceRef(Font2D font) { this.font2D = font; @@ -561,16 +557,7 @@ public class HBShaper { private void createFace() { try { - get_table_data_fn = getBoundUpcallStub(Arena.ofAuto(), - Font2D.class, - font2D, // bind arg - "getFontTableData", // method name - JAVA_INT, // return type - JAVA_INT, ADDRESS); // arg types - if (get_table_data_fn == null) { - return; - } - face = (MemorySegment)create_face_handle.invokeExact(get_table_data_fn); + face = (MemorySegment)create_face_handle.invokeExact(get_table_data_fn_stub); } catch (Throwable t) { } } diff --git a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java index 9815d657eee..891a15f24de 100644 --- a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java +++ b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, 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 @@ -250,11 +250,6 @@ public final class SunGraphics2D private FontInfo glyphVectorFontInfo; private FontRenderContext glyphVectorFRC; - private static final int slowTextTransformMask = - AffineTransform.TYPE_GENERAL_TRANSFORM - | AffineTransform.TYPE_MASK_ROTATION - | AffineTransform.TYPE_FLIP; - static { if (PerformanceLogger.loggingEnabled()) { PerformanceLogger.setTime("SunGraphics2D static initialization"); @@ -453,13 +448,13 @@ public final class SunGraphics2D * or whether that shape must be "kept" unmodified. */ Shape intersectShapes(Shape s1, Shape s2, boolean keep1, boolean keep2) { - if (s1 instanceof Rectangle && s2 instanceof Rectangle) { - return ((Rectangle) s1).intersection((Rectangle) s2); + if (s1 instanceof Rectangle r1 && s2 instanceof Rectangle r2) { + return r1.intersection(r2); } - if (s1 instanceof Rectangle2D) { - return intersectRectShape((Rectangle2D) s1, s2, keep1, keep2); - } else if (s2 instanceof Rectangle2D) { - return intersectRectShape((Rectangle2D) s2, s1, keep2, keep1); + if (s1 instanceof Rectangle2D r1) { + return intersectRectShape(r1, s2, keep1, keep2); + } else if (s2 instanceof Rectangle2D r2) { + return intersectRectShape(r2, s1, keep2, keep1); } return intersectByArea(s1, s2, keep1, keep2); } @@ -473,8 +468,7 @@ public final class SunGraphics2D */ Shape intersectRectShape(Rectangle2D r, Shape s, boolean keep1, boolean keep2) { - if (s instanceof Rectangle2D) { - Rectangle2D r2 = (Rectangle2D) s; + if (s instanceof Rectangle2D r2) { Rectangle2D outrect; if (!keep1) { outrect = r; @@ -596,12 +590,10 @@ public final class SunGraphics2D } float ptSize = font.getSize2D(); - int txFontType; AffineTransform devAt, textAt=null; if (font.isTransformed()) { textAt = font.getTransform(); textAt.scale(ptSize, ptSize); - txFontType = textAt.getType(); info.originX = (float)textAt.getTranslateX(); info.originY = (float)textAt.getTranslateY(); textAt.translate(-info.originX, -info.originY); @@ -621,7 +613,6 @@ public final class SunGraphics2D } info.pixelHeight = (int)(Math.abs(scaley)+0.5); } else { - txFontType = AffineTransform.TYPE_IDENTITY; info.originX = info.originY = 0; if (transformState >= TRANSFORM_TRANSLATESCALE) { transform.getMatrix(info.devTx = new double[4]); @@ -783,18 +774,6 @@ public final class SunGraphics2D return info; } - public static boolean isRotated(double [] mtx) { - if ((mtx[0] == mtx[3]) && - (mtx[1] == 0.0) && - (mtx[2] == 0.0) && - (mtx[0] > 0.0)) - { - return false; - } - - return true; - } - public void setFont(Font font) { /* replacing the reference equality test font != this.font with * !font.equals(this.font) did not yield any measurable difference @@ -944,8 +923,7 @@ public final class SunGraphics2D } int newCompState; CompositeType newCompType; - if (comp instanceof AlphaComposite) { - AlphaComposite alphacomp = (AlphaComposite) comp; + if (comp instanceof AlphaComposite alphacomp) { newCompType = CompositeType.forAlphaComposite(alphacomp); if (newCompType == CompositeType.SrcOverNoEa) { if (paintState == PAINT_OPAQUECOLOR || @@ -1000,8 +978,8 @@ public final class SunGraphics2D * @see TexturePaint */ public void setPaint(Paint paint) { - if (paint instanceof Color) { - setColor((Color) paint); + if (paint instanceof Color c) { + setColor(c); return; } if (paint == null || this.paint == paint) { @@ -1162,8 +1140,8 @@ public final class SunGraphics2D } int saveStrokeState = strokeState; stroke = s; - if (s instanceof BasicStroke) { - validateBasicStroke((BasicStroke) s); + if (s instanceof BasicStroke bs) { + validateBasicStroke(bs); } else { strokeState = STROKE_CUSTOM; } @@ -1193,11 +1171,10 @@ public final class SunGraphics2D throw new IllegalArgumentException (hintValue+" is not compatible with "+hintKey); } - if (hintKey instanceof SunHints.Key) { + if (hintKey instanceof SunHints.Key sunKey) { boolean stateChanged; boolean textStateChanged = false; boolean recognized = true; - SunHints.Key sunKey = (SunHints.Key) hintKey; int newHint; if (sunKey == SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST) { newHint = ((Integer)hintValue).intValue(); @@ -1297,7 +1274,6 @@ public final class SunGraphics2D hints.put(hintKey, hintValue); } - /** * Returns the preferences for the rendering algorithms. * @param hintKey The category of hint to be set. The strings @@ -1310,10 +1286,10 @@ public final class SunGraphics2D if (hints != null) { return hints.get(hintKey); } - if (!(hintKey instanceof SunHints.Key)) { + if (!(hintKey instanceof SunHints.Key shk)) { return null; } - int keyindex = ((SunHints.Key)hintKey).getIndex(); + int keyindex = shk.getIndex(); switch (keyindex) { case SunHints.INTKEY_RENDERING: return SunHints.Value.get(SunHints.INTKEY_RENDERING, @@ -1822,8 +1798,8 @@ public final class SunGraphics2D public Rectangle getClipBounds(Rectangle r) { if (clipState != CLIP_DEVICE) { if (transformState <= TRANSFORM_INT_TRANSLATE) { - if (usrClip instanceof Rectangle) { - r.setBounds((Rectangle) usrClip); + if (usrClip instanceof Rectangle usrClipRect) { + r.setBounds(usrClipRect); } else { r.setFrame(usrClip.getBounds2D()); } @@ -1970,8 +1946,7 @@ public final class SunGraphics2D r.translate(tx, ty); return r; } - if (s instanceof Rectangle2D) { - Rectangle2D rect = (Rectangle2D) s; + if (s instanceof Rectangle2D rect) { return new Rectangle2D.Double(rect.getX() + tx, rect.getY() + ty, rect.getWidth(), @@ -1991,10 +1966,9 @@ public final class SunGraphics2D return null; } - if (clip instanceof Rectangle2D && + if (clip instanceof Rectangle2D rect && (tx.getType() & NON_RECTILINEAR_TRANSFORM_MASK) == 0) { - Rectangle2D rect = (Rectangle2D) clip; double[] matrix = new double[4]; matrix[0] = rect.getX(); matrix[1] = rect.getY(); @@ -2180,65 +2154,6 @@ public final class SunGraphics2D } } - /* - public void XcopyArea(int x, int y, int w, int h, int dx, int dy) { - Rectangle rect = new Rectangle(x, y, w, h); - rect = transformBounds(rect, transform); - Point2D point = new Point2D.Float(dx, dy); - Point2D root = new Point2D.Float(0, 0); - point = transform.transform(point, point); - root = transform.transform(root, root); - int fdx = (int)(point.getX()-root.getX()); - int fdy = (int)(point.getY()-root.getY()); - - Rectangle r = getCompBounds().intersection(rect.getBounds()); - - if (r.isEmpty()) { - return; - } - - // Begin Rasterizer for Clip Shape - boolean skipClip = true; - byte[] clipAlpha = null; - - if (clipState == CLIP_SHAPE) { - - int box[] = new int[4]; - - clipRegion.getBounds(box); - Rectangle devR = new Rectangle(box[0], box[1], - box[2] - box[0], - box[3] - box[1]); - if (!devR.isEmpty()) { - OutputManager mgr = getOutputManager(); - RegionIterator ri = clipRegion.getIterator(); - while (ri.nextYRange(box)) { - int spany = box[1]; - int spanh = box[3] - spany; - while (ri.nextXBand(box)) { - int spanx = box[0]; - int spanw = box[2] - spanx; - mgr.copyArea(this, null, - spanw, 0, - spanx, spany, - spanw, spanh, - fdx, fdy, - null); - } - } - } - return; - } - // End Rasterizer for Clip Shape - - getOutputManager().copyArea(this, null, - r.width, 0, - r.x, r.y, r.width, - r.height, fdx, fdy, - null); - } - */ - public void drawLine(int x1, int y1, int x2, int y2) { try { drawpipe.drawLine(this, x1, y1, x2, y2); @@ -2465,8 +2380,8 @@ public final class SunGraphics2D if (paintState <= PAINT_ALPHACOLOR) { validateColor(); } - if (composite instanceof XORComposite) { - Color c = ((XORComposite) composite).getXorColor(); + if (composite instanceof XORComposite xorComp) { + Color c = xorComp.getXorColor(); setComposite(new XORComposite(c, surfaceData)); } validatePipe(); @@ -2668,8 +2583,7 @@ public final class SunGraphics2D } // BufferedImage case: use a simple drawImage call - if (img instanceof BufferedImage) { - BufferedImage bufImg = (BufferedImage)img; + if (img instanceof BufferedImage bufImg) { drawImage(bufImg,xform,null); return; } @@ -2905,21 +2819,6 @@ public final class SunGraphics2D drawRenderedImage(rendering,reverseTransform); } - - - /* - * Transform the bounding box of the BufferedImage - */ - protected Rectangle transformBounds(Rectangle rect, - AffineTransform tx) { - if (tx.isIdentity()) { - return rect; - } - - Shape s = transformShape(tx, rect); - return s.getBounds(); - } - // text rendering methods public void drawString(String str, int x, int y) { if (str == null) { @@ -3130,13 +3029,12 @@ public final class SunGraphics2D invalidateTransform(); } return result; - } else if (img instanceof MultiResolutionImage) { + } else if (img instanceof MultiResolutionImage mrImage) { // get scaled destination image size int width = img.getWidth(observer); int height = img.getHeight(observer); - MultiResolutionImage mrImage = (MultiResolutionImage) img; Image resolutionVariant = getResolutionVariant(mrImage, width, height, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, @@ -3311,8 +3209,7 @@ public final class SunGraphics2D Image resolutionVariant = img.getResolutionVariant(destImageWidth, destImageHeight); - if (resolutionVariant instanceof ToolkitImage - && ((ToolkitImage) resolutionVariant).hasError()) { + if (resolutionVariant instanceof ToolkitImage tki && tki.hasError()) { return null; } diff --git a/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java b/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java index d552bc5c9f2..8c6d6ab9850 100644 --- a/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java +++ b/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java @@ -44,6 +44,7 @@ import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Toolkit; +import java.awt.event.FocusEvent; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; @@ -795,7 +796,7 @@ public class SwingUtilities2 { */ public static void adjustFocus(JComponent c) { if (!c.hasFocus() && c.isRequestFocusEnabled()) { - c.requestFocus(); + c.requestFocus(FocusEvent.Cause.MOUSE_EVENT); } } @@ -1434,15 +1435,6 @@ public class SwingUtilities2 { } } - /** - * checks if the system clipboard can be accessed. - * This is true in a headful environment, false in a headless one - * - */ - public static boolean canAccessSystemClipboard() { - return !GraphicsEnvironment.isHeadless(); - } - public static String displayPropertiesToCSS(Font font, Color fg) { StringBuilder rule = new StringBuilder("body {"); if (font != null) { @@ -1645,24 +1637,24 @@ public class SwingUtilities2 { if (container.isFocusCycleRoot()) { FocusTraversalPolicy policy = container.getFocusTraversalPolicy(); Component comp = policy.getDefaultComponent(container); - if (comp!=null) { - comp.requestFocus(); + if (comp != null) { + comp.requestFocus(FocusEvent.Cause.TRAVERSAL); return comp; } } Container rootAncestor = container.getFocusCycleRootAncestor(); - if (rootAncestor!=null) { + if (rootAncestor != null) { FocusTraversalPolicy policy = rootAncestor.getFocusTraversalPolicy(); Component comp = policy.getComponentAfter(rootAncestor, container); - if (comp!=null && SwingUtilities.isDescendingFrom(comp, container)) { - comp.requestFocus(); + if (comp != null && SwingUtilities.isDescendingFrom(comp, container)) { + comp.requestFocus(FocusEvent.Cause.TRAVERSAL); return comp; } } } if (component.isFocusable()) { - component.requestFocus(); + component.requestFocus(FocusEvent.Cause.TRAVERSAL); return component; } return null; diff --git a/src/java.desktop/share/legal/giflib.md b/src/java.desktop/share/legal/giflib.md index 5697dc7ca9a..781023dd334 100644 --- a/src/java.desktop/share/legal/giflib.md +++ b/src/java.desktop/share/legal/giflib.md @@ -1,9 +1,9 @@ -## GIFLIB v5.2.2 +## GIFLIB v6.1.2 ### GIFLIB License ``` -The GIFLIB distribution is Copyright (c) 1997 Eric S. Raymond += MIT LICENSE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -25,9 +25,15 @@ THE SOFTWARE. --------------------------------- The below applies to the following file(s): +giflib/dgif_lib.c +giflib/gifalloc.c +giflib/gif_err.c giflib/openbsd-reallocarray.c +Copyright (C) 1989 Gershon Elber Copyright (C) 2008 Otto Moerbeek +Copyright (C) Eric S. Raymond + SPDX-License-Identifier: MIT diff --git a/src/java.desktop/share/native/libsplashscreen/giflib/COPYING b/src/java.desktop/share/native/libsplashscreen/giflib/COPYING index b9c0b501260..92774a3b036 100644 --- a/src/java.desktop/share/native/libsplashscreen/giflib/COPYING +++ b/src/java.desktop/share/native/libsplashscreen/giflib/COPYING @@ -1,4 +1,4 @@ -The GIFLIB distribution is Copyright (c) 1997 Eric S. Raymond += MIT LICENSE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/java.desktop/share/native/libsplashscreen/giflib/dgif_lib.c b/src/java.desktop/share/native/libsplashscreen/giflib/dgif_lib.c index 0b2860b4b50..2fa005ed20d 100644 --- a/src/java.desktop/share/native/libsplashscreen/giflib/dgif_lib.c +++ b/src/java.desktop/share/native/libsplashscreen/giflib/dgif_lib.c @@ -30,9 +30,9 @@ The functions here and in egif_lib.c are partitioned carefully so that if you only require one of read and write capability, only one of these two modules will be linked. Preserve this property! -SPDX-License-Identifier: MIT - *****************************************************************************/ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: Copyright (C) Eric S. Raymond #include #include @@ -55,11 +55,11 @@ SPDX-License-Identifier: MIT /* avoid extra function call in case we use fread (TVT) */ static int InternalRead(GifFileType *gif, GifByteType *buf, int len) { - // fprintf(stderr, "### Read: %d\n", len); - return (((GifFilePrivateType *)gif->Private)->Read - ? ((GifFilePrivateType *)gif->Private)->Read(gif, buf, len) - : fread(buf, 1, len, - ((GifFilePrivateType *)gif->Private)->File)); + // fprintf(stderr, "### Read: %d\n", len); + return (((GifFilePrivateType *)gif->Private)->Read + ? ((GifFilePrivateType *)gif->Private)->Read(gif, buf, len) + : fread(buf, 1, len, + ((GifFilePrivateType *)gif->Private)->File)); } static int DGifGetWord(GifFileType *GifFile, GifWord *Word); @@ -78,18 +78,18 @@ static int DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, info record. ******************************************************************************/ GifFileType *DGifOpenFileName(const char *FileName, int *Error) { - int FileHandle; - GifFileType *GifFile; + int FileHandle; + GifFileType *GifFile; - if ((FileHandle = open(FileName, O_RDONLY)) == -1) { - if (Error != NULL) { - *Error = D_GIF_ERR_OPEN_FAILED; + if ((FileHandle = open(FileName, O_RDONLY)) == -1) { + if (Error != NULL) { + *Error = D_GIF_ERR_OPEN_FAILED; + } + return NULL; } - return NULL; - } - GifFile = DGifOpenFileHandle(FileHandle, Error); - return GifFile; + GifFile = DGifOpenFileHandle(FileHandle, Error); + return GifFile; } /****************************************************************************** @@ -98,171 +98,171 @@ GifFileType *DGifOpenFileName(const char *FileName, int *Error) { info record. ******************************************************************************/ GifFileType *DGifOpenFileHandle(int FileHandle, int *Error) { - char Buf[GIF_STAMP_LEN + 1]; - GifFileType *GifFile; - GifFilePrivateType *Private; - FILE *f; + char Buf[GIF_STAMP_LEN + 1]; + GifFileType *GifFile; + GifFilePrivateType *Private; + FILE *f; - GifFile = (GifFileType *)malloc(sizeof(GifFileType)); - if (GifFile == NULL) { - if (Error != NULL) { - *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + if (Error != NULL) { + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + } + (void)close(FileHandle); + return NULL; } - (void)close(FileHandle); - return NULL; - } - /*@i1@*/ memset(GifFile, '\0', sizeof(GifFileType)); + /*@i1@*/ memset(GifFile, '\0', sizeof(GifFileType)); - /* Belt and suspenders, in case the null pointer isn't zero */ - GifFile->SavedImages = NULL; - GifFile->SColorMap = NULL; + /* Belt and suspenders, in case the null pointer isn't zero */ + GifFile->SavedImages = NULL; + GifFile->SColorMap = NULL; - Private = (GifFilePrivateType *)calloc(1, sizeof(GifFilePrivateType)); - if (Private == NULL) { - if (Error != NULL) { - *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + Private = (GifFilePrivateType *)calloc(1, sizeof(GifFilePrivateType)); + if (Private == NULL) { + if (Error != NULL) { + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + } + (void)close(FileHandle); + free((char *)GifFile); + return NULL; } - (void)close(FileHandle); - free((char *)GifFile); - return NULL; - } - /*@i1@*/ memset(Private, '\0', sizeof(GifFilePrivateType)); + /*@i1@*/ memset(Private, '\0', sizeof(GifFilePrivateType)); #ifdef _WIN32 - _setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */ + _setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */ #endif /* _WIN32 */ - f = fdopen(FileHandle, "rb"); /* Make it into a stream: */ + f = fdopen(FileHandle, "rb"); /* Make it into a stream: */ - /*@-mustfreeonly@*/ - GifFile->Private = (void *)Private; - Private->FileHandle = FileHandle; - Private->File = f; - Private->FileState = FILE_STATE_READ; - Private->Read = NULL; /* don't use alternate input method (TVT) */ - GifFile->UserData = NULL; /* TVT */ - /*@=mustfreeonly@*/ + /*@-mustfreeonly@*/ + GifFile->Private = (void *)Private; + Private->FileHandle = FileHandle; + Private->File = f; + Private->FileState = FILE_STATE_READ; + Private->Read = NULL; /* don't use alternate input method (TVT) */ + GifFile->UserData = NULL; /* TVT */ + /*@=mustfreeonly@*/ - /* Let's see if this is a GIF file: */ - /* coverity[check_return] */ - if (InternalRead(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != - GIF_STAMP_LEN) { - if (Error != NULL) { - *Error = D_GIF_ERR_READ_FAILED; + /* Let's see if this is a GIF file: */ + /* coverity[check_return] */ + if (InternalRead(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != + GIF_STAMP_LEN) { + if (Error != NULL) { + *Error = D_GIF_ERR_READ_FAILED; + } + (void)fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; } - (void)fclose(f); - free((char *)Private); - free((char *)GifFile); - return NULL; - } - /* Check for GIF prefix at start of file */ - Buf[GIF_STAMP_LEN] = 0; - if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { - if (Error != NULL) { - *Error = D_GIF_ERR_NOT_GIF_FILE; + /* Check for GIF prefix at start of file */ + Buf[GIF_STAMP_LEN] = 0; + if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { + if (Error != NULL) { + *Error = D_GIF_ERR_NOT_GIF_FILE; + } + (void)fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; } - (void)fclose(f); - free((char *)Private); - free((char *)GifFile); - return NULL; - } - if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { - (void)fclose(f); - free((char *)Private); - free((char *)GifFile); - return NULL; - } + if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { + (void)fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } - GifFile->Error = 0; + GifFile->Error = 0; - /* What version of GIF? */ - Private->gif89 = (Buf[GIF_VERSION_POS + 1] == '9'); + /* What version of GIF? */ + Private->gif89 = (Buf[GIF_VERSION_POS + 1] == '9'); - return GifFile; + return GifFile; } /****************************************************************************** GifFileType constructor with user supplied input function (TVT) ******************************************************************************/ GifFileType *DGifOpen(void *userData, InputFunc readFunc, int *Error) { - char Buf[GIF_STAMP_LEN + 1]; - GifFileType *GifFile; - GifFilePrivateType *Private; + char Buf[GIF_STAMP_LEN + 1]; + GifFileType *GifFile; + GifFilePrivateType *Private; - GifFile = (GifFileType *)malloc(sizeof(GifFileType)); - if (GifFile == NULL) { - if (Error != NULL) { - *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + if (Error != NULL) { + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + } + return NULL; } - return NULL; - } - memset(GifFile, '\0', sizeof(GifFileType)); + memset(GifFile, '\0', sizeof(GifFileType)); - /* Belt and suspenders, in case the null pointer isn't zero */ - GifFile->SavedImages = NULL; - GifFile->SColorMap = NULL; + /* Belt and suspenders, in case the null pointer isn't zero */ + GifFile->SavedImages = NULL; + GifFile->SColorMap = NULL; - Private = (GifFilePrivateType *)calloc(1, sizeof(GifFilePrivateType)); - if (!Private) { - if (Error != NULL) { - *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + Private = (GifFilePrivateType *)calloc(1, sizeof(GifFilePrivateType)); + if (!Private) { + if (Error != NULL) { + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + } + free((char *)GifFile); + return NULL; } - free((char *)GifFile); - return NULL; - } - /*@i1@*/ memset(Private, '\0', sizeof(GifFilePrivateType)); + /*@i1@*/ memset(Private, '\0', sizeof(GifFilePrivateType)); - GifFile->Private = (void *)Private; - Private->FileHandle = 0; - Private->File = NULL; - Private->FileState = FILE_STATE_READ; + GifFile->Private = (void *)Private; + Private->FileHandle = 0; + Private->File = NULL; + Private->FileState = FILE_STATE_READ; - Private->Read = readFunc; /* TVT */ - GifFile->UserData = userData; /* TVT */ + Private->Read = readFunc; /* TVT */ + GifFile->UserData = userData; /* TVT */ - /* Lets see if this is a GIF file: */ - /* coverity[check_return] */ - if (InternalRead(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != - GIF_STAMP_LEN) { - if (Error != NULL) { - *Error = D_GIF_ERR_READ_FAILED; + /* Lets see if this is a GIF file: */ + /* coverity[check_return] */ + if (InternalRead(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != + GIF_STAMP_LEN) { + if (Error != NULL) { + *Error = D_GIF_ERR_READ_FAILED; + } + free((char *)Private); + free((char *)GifFile); + return NULL; } - free((char *)Private); - free((char *)GifFile); - return NULL; - } - /* Check for GIF prefix at start of file */ - Buf[GIF_STAMP_LEN] = '\0'; - if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { - if (Error != NULL) { - *Error = D_GIF_ERR_NOT_GIF_FILE; + /* Check for GIF prefix at start of file */ + Buf[GIF_STAMP_LEN] = '\0'; + if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { + if (Error != NULL) { + *Error = D_GIF_ERR_NOT_GIF_FILE; + } + free((char *)Private); + free((char *)GifFile); + return NULL; } - free((char *)Private); - free((char *)GifFile); - return NULL; - } - if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { - free((char *)Private); - free((char *)GifFile); - if (Error != NULL) { - *Error = D_GIF_ERR_NO_SCRN_DSCR; + if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { + free((char *)Private); + free((char *)GifFile); + if (Error != NULL) { + *Error = D_GIF_ERR_NO_SCRN_DSCR; + } + return NULL; } - return NULL; - } - GifFile->Error = 0; + GifFile->Error = 0; - /* What version of GIF? */ - Private->gif89 = (Buf[GIF_VERSION_POS + 1] == '9'); + /* What version of GIF? */ + Private->gif89 = (Buf[GIF_VERSION_POS + 1] == '9'); - return GifFile; + return GifFile; } /****************************************************************************** @@ -270,180 +270,180 @@ GifFileType *DGifOpen(void *userData, InputFunc readFunc, int *Error) { this routine is called automatically from DGif file open routines. ******************************************************************************/ int DGifGetScreenDesc(GifFileType *GifFile) { - int BitsPerPixel; - bool SortFlag; - GifByteType Buf[3]; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + int BitsPerPixel; + bool SortFlag; + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - GifFile->Error = D_GIF_ERR_NOT_READABLE; - return GIF_ERROR; - } - - /* Put the screen descriptor into the file: */ - if (DGifGetWord(GifFile, &GifFile->SWidth) == GIF_ERROR || - DGifGetWord(GifFile, &GifFile->SHeight) == GIF_ERROR) { - return GIF_ERROR; - } - - if (InternalRead(GifFile, Buf, 3) != 3) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - GifFreeMapObject(GifFile->SColorMap); - GifFile->SColorMap = NULL; - return GIF_ERROR; - } - GifFile->SColorResolution = (((Buf[0] & 0x70) + 1) >> 4) + 1; - SortFlag = (Buf[0] & 0x08) != 0; - BitsPerPixel = (Buf[0] & 0x07) + 1; - GifFile->SBackGroundColor = Buf[1]; - GifFile->AspectByte = Buf[2]; - if (Buf[0] & 0x80) { /* Do we have global color map? */ - int i; - - GifFile->SColorMap = GifMakeMapObject(1 << BitsPerPixel, NULL); - if (GifFile->SColorMap == NULL) { - GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; - return GIF_ERROR; + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; } - /* Get the global color map: */ - GifFile->SColorMap->SortFlag = SortFlag; - for (i = 0; i < GifFile->SColorMap->ColorCount; i++) { - /* coverity[check_return] */ - if (InternalRead(GifFile, Buf, 3) != 3) { + /* Put the screen descriptor into the file: */ + if (DGifGetWord(GifFile, &GifFile->SWidth) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->SHeight) == GIF_ERROR) { + return GIF_ERROR; + } + + if (InternalRead(GifFile, Buf, 3) != 3) { + GifFile->Error = D_GIF_ERR_READ_FAILED; GifFreeMapObject(GifFile->SColorMap); GifFile->SColorMap = NULL; - GifFile->Error = D_GIF_ERR_READ_FAILED; return GIF_ERROR; - } - GifFile->SColorMap->Colors[i].Red = Buf[0]; - GifFile->SColorMap->Colors[i].Green = Buf[1]; - GifFile->SColorMap->Colors[i].Blue = Buf[2]; } - } else { - GifFile->SColorMap = NULL; - } + GifFile->SColorResolution = (((Buf[0] & 0x70) + 1) >> 4) + 1; + SortFlag = (Buf[0] & 0x08) != 0; + BitsPerPixel = (Buf[0] & 0x07) + 1; + GifFile->SBackGroundColor = Buf[1]; + GifFile->AspectByte = Buf[2]; + if (Buf[0] & 0x80) { /* Do we have global color map? */ + int i; - /* - * No check here for whether the background color is in range for the - * screen color map. Possibly there should be. - */ + GifFile->SColorMap = GifMakeMapObject(1 << BitsPerPixel, NULL); + if (GifFile->SColorMap == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } - return GIF_OK; + /* Get the global color map: */ + GifFile->SColorMap->SortFlag = SortFlag; + for (i = 0; i < GifFile->SColorMap->ColorCount; i++) { + /* coverity[check_return] */ + if (InternalRead(GifFile, Buf, 3) != 3) { + GifFreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + GifFile->SColorMap->Colors[i].Red = Buf[0]; + GifFile->SColorMap->Colors[i].Green = Buf[1]; + GifFile->SColorMap->Colors[i].Blue = Buf[2]; + } + } else { + GifFile->SColorMap = NULL; + } + + /* + * No check here for whether the background color is in range for the + * screen color map. Possibly there should be. + */ + + return GIF_OK; } const char *DGifGetGifVersion(GifFileType *GifFile) { - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - if (Private->gif89) { - return GIF89_STAMP; - } else { - return GIF87_STAMP; - } + if (Private->gif89) { + return GIF89_STAMP; + } else { + return GIF87_STAMP; + } } /****************************************************************************** This routine should be called before any attempt to read an image. ******************************************************************************/ int DGifGetRecordType(GifFileType *GifFile, GifRecordType *Type) { - GifByteType Buf; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - GifFile->Error = D_GIF_ERR_NOT_READABLE; - return GIF_ERROR; - } + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } - /* coverity[check_return] */ - if (InternalRead(GifFile, &Buf, 1) != 1) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; - } + /* coverity[check_return] */ + if (InternalRead(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } - // fprintf(stderr, "### DGifGetRecordType: %02x\n", Buf); - switch (Buf) { - case DESCRIPTOR_INTRODUCER: - *Type = IMAGE_DESC_RECORD_TYPE; - break; - case EXTENSION_INTRODUCER: - *Type = EXTENSION_RECORD_TYPE; - break; - case TERMINATOR_INTRODUCER: - *Type = TERMINATE_RECORD_TYPE; - break; - default: - *Type = UNDEFINED_RECORD_TYPE; - GifFile->Error = D_GIF_ERR_WRONG_RECORD; - return GIF_ERROR; - } + // fprintf(stderr, "### DGifGetRecordType: %02x\n", Buf); + switch (Buf) { + case DESCRIPTOR_INTRODUCER: + *Type = IMAGE_DESC_RECORD_TYPE; + break; + case EXTENSION_INTRODUCER: + *Type = EXTENSION_RECORD_TYPE; + break; + case TERMINATOR_INTRODUCER: + *Type = TERMINATE_RECORD_TYPE; + break; + default: + *Type = UNDEFINED_RECORD_TYPE; + GifFile->Error = D_GIF_ERR_WRONG_RECORD; + return GIF_ERROR; + } - return GIF_OK; + return GIF_OK; } int DGifGetImageHeader(GifFileType *GifFile) { - unsigned int BitsPerPixel; - GifByteType Buf[3]; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + unsigned int BitsPerPixel; + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - GifFile->Error = D_GIF_ERR_NOT_READABLE; - return GIF_ERROR; - } - - if (DGifGetWord(GifFile, &GifFile->Image.Left) == GIF_ERROR || - DGifGetWord(GifFile, &GifFile->Image.Top) == GIF_ERROR || - DGifGetWord(GifFile, &GifFile->Image.Width) == GIF_ERROR || - DGifGetWord(GifFile, &GifFile->Image.Height) == GIF_ERROR) { - return GIF_ERROR; - } - if (InternalRead(GifFile, Buf, 1) != 1) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - GifFreeMapObject(GifFile->Image.ColorMap); - GifFile->Image.ColorMap = NULL; - return GIF_ERROR; - } - BitsPerPixel = (Buf[0] & 0x07) + 1; - GifFile->Image.Interlace = (Buf[0] & 0x40) ? true : false; - - /* Setup the colormap */ - if (GifFile->Image.ColorMap) { - GifFreeMapObject(GifFile->Image.ColorMap); - GifFile->Image.ColorMap = NULL; - } - /* Does this image have local color map? */ - if (Buf[0] & 0x80) { - unsigned int i; - - GifFile->Image.ColorMap = - GifMakeMapObject(1 << BitsPerPixel, NULL); - if (GifFile->Image.ColorMap == NULL) { - GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; - return GIF_ERROR; + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; } - /* Get the image local color map: */ - for (i = 0; i < GifFile->Image.ColorMap->ColorCount; i++) { - /* coverity[check_return] */ - if (InternalRead(GifFile, Buf, 3) != 3) { - GifFreeMapObject(GifFile->Image.ColorMap); + if (DGifGetWord(GifFile, &GifFile->Image.Left) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Top) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Width) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Height) == GIF_ERROR) { + return GIF_ERROR; + } + if (InternalRead(GifFile, Buf, 1) != 1) { GifFile->Error = D_GIF_ERR_READ_FAILED; + GifFreeMapObject(GifFile->Image.ColorMap); GifFile->Image.ColorMap = NULL; return GIF_ERROR; - } - GifFile->Image.ColorMap->Colors[i].Red = Buf[0]; - GifFile->Image.ColorMap->Colors[i].Green = Buf[1]; - GifFile->Image.ColorMap->Colors[i].Blue = Buf[2]; } - } + BitsPerPixel = (Buf[0] & 0x07) + 1; + GifFile->Image.Interlace = (Buf[0] & 0x40) ? true : false; - Private->PixelCount = - (long)GifFile->Image.Width * (long)GifFile->Image.Height; + /* Setup the colormap */ + if (GifFile->Image.ColorMap) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + /* Does this image have local color map? */ + if (Buf[0] & 0x80) { + unsigned int i; - /* Reset decompress algorithm parameters. */ - return DGifSetupDecompress(GifFile); + GifFile->Image.ColorMap = + GifMakeMapObject(1 << BitsPerPixel, NULL); + if (GifFile->Image.ColorMap == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + + /* Get the image local color map: */ + for (i = 0; i < GifFile->Image.ColorMap->ColorCount; i++) { + /* coverity[check_return] */ + if (InternalRead(GifFile, Buf, 3) != 3) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Error = D_GIF_ERR_READ_FAILED; + GifFile->Image.ColorMap = NULL; + return GIF_ERROR; + } + GifFile->Image.ColorMap->Colors[i].Red = Buf[0]; + GifFile->Image.ColorMap->Colors[i].Green = Buf[1]; + GifFile->Image.ColorMap->Colors[i].Blue = Buf[2]; + } + } + + Private->PixelCount = + (long)GifFile->Image.Width * (long)GifFile->Image.Height; + + /* Reset decompress algorithm parameters. */ + return DGifSetupDecompress(GifFile); } /****************************************************************************** @@ -451,133 +451,135 @@ int DGifGetImageHeader(GifFileType *GifFile) { Note it is assumed the Image desc. header has been read. ******************************************************************************/ int DGifGetImageDesc(GifFileType *GifFile) { - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - SavedImage *sp; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + SavedImage *sp; - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - GifFile->Error = D_GIF_ERR_NOT_READABLE; - return GIF_ERROR; - } - - if (DGifGetImageHeader(GifFile) == GIF_ERROR) { - return GIF_ERROR; - } - - if (GifFile->SavedImages) { - SavedImage *new_saved_images = (SavedImage *)reallocarray( - GifFile->SavedImages, (GifFile->ImageCount + 1), - sizeof(SavedImage)); - if (new_saved_images == NULL) { - GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; - return GIF_ERROR; + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; } - GifFile->SavedImages = new_saved_images; - } else { - if ((GifFile->SavedImages = - (SavedImage *)malloc(sizeof(SavedImage))) == NULL) { - GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; - return GIF_ERROR; + + if (DGifGetImageHeader(GifFile) == GIF_ERROR) { + return GIF_ERROR; } - } - sp = &GifFile->SavedImages[GifFile->ImageCount]; - memcpy(&sp->ImageDesc, &GifFile->Image, sizeof(GifImageDesc)); - if (GifFile->Image.ColorMap != NULL) { - sp->ImageDesc.ColorMap = - GifMakeMapObject(GifFile->Image.ColorMap->ColorCount, - GifFile->Image.ColorMap->Colors); - if (sp->ImageDesc.ColorMap == NULL) { - GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; - return GIF_ERROR; + if (GifFile->SavedImages) { + SavedImage *new_saved_images = (SavedImage *)reallocarray( + GifFile->SavedImages, (GifFile->ImageCount + 1), + sizeof(SavedImage)); + if (new_saved_images == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + GifFile->SavedImages = new_saved_images; + } else { + if ((GifFile->SavedImages = + (SavedImage *)malloc(sizeof(SavedImage))) == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } } - } - sp->RasterBits = (unsigned char *)NULL; - sp->ExtensionBlockCount = 0; - sp->ExtensionBlocks = (ExtensionBlock *)NULL; - GifFile->ImageCount++; + sp = &GifFile->SavedImages[GifFile->ImageCount]; + memcpy(&sp->ImageDesc, &GifFile->Image, sizeof(GifImageDesc)); + if (GifFile->Image.ColorMap != NULL) { + sp->ImageDesc.ColorMap = + GifMakeMapObject(GifFile->Image.ColorMap->ColorCount, + GifFile->Image.ColorMap->Colors); + if (sp->ImageDesc.ColorMap == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } + sp->RasterBits = (unsigned char *)NULL; + sp->ExtensionBlockCount = 0; + sp->ExtensionBlocks = (ExtensionBlock *)NULL; - return GIF_OK; + GifFile->ImageCount++; + + return GIF_OK; } /****************************************************************************** Get one full scanned line (Line) of length LineLen from GIF file. ******************************************************************************/ int DGifGetLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) { - GifByteType *Dummy; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + GifByteType *Dummy; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - GifFile->Error = D_GIF_ERR_NOT_READABLE; - return GIF_ERROR; - } - - if (!LineLen) { - LineLen = GifFile->Image.Width; - } - - if ((Private->PixelCount -= LineLen) > 0xffff0000UL) { - GifFile->Error = D_GIF_ERR_DATA_TOO_BIG; - return GIF_ERROR; - } - - if (DGifDecompressLine(GifFile, Line, LineLen) == GIF_OK) { - if (Private->PixelCount == 0) { - /* We probably won't be called any more, so let's clean - * up everything before we return: need to flush out all - * the rest of image until an empty block (size 0) - * detected. We use GetCodeNext. - */ - do { - if (DGifGetCodeNext(GifFile, &Dummy) == - GIF_ERROR) { - return GIF_ERROR; - } - } while (Dummy != NULL); + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (!LineLen) { + LineLen = GifFile->Image.Width; + } + + if (LineLen < 0 || Private->PixelCount < (unsigned long)LineLen) { + GifFile->Error = D_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + Private->PixelCount -= LineLen; + + if (DGifDecompressLine(GifFile, Line, LineLen) == GIF_OK) { + if (Private->PixelCount == 0) { + /* We probably won't be called any more, so let's clean + * up everything before we return: need to flush out all + * the rest of image until an empty block (size 0) + * detected. We use GetCodeNext. + */ + do { + if (DGifGetCodeNext(GifFile, &Dummy) == + GIF_ERROR) { + return GIF_ERROR; + } + } while (Dummy != NULL); + } + return GIF_OK; + } else { + return GIF_ERROR; } - return GIF_OK; - } else { - return GIF_ERROR; - } } /****************************************************************************** Put one pixel (Pixel) into GIF file. ******************************************************************************/ int DGifGetPixel(GifFileType *GifFile, GifPixelType Pixel) { - GifByteType *Dummy; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + GifByteType *Dummy; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - GifFile->Error = D_GIF_ERR_NOT_READABLE; - return GIF_ERROR; - } - if (--Private->PixelCount > 0xffff0000UL) { - GifFile->Error = D_GIF_ERR_DATA_TOO_BIG; - return GIF_ERROR; - } - - if (DGifDecompressLine(GifFile, &Pixel, 1) == GIF_OK) { - if (Private->PixelCount == 0) { - /* We probably won't be called any more, so let's clean - * up everything before we return: need to flush out all - * the rest of image until an empty block (size 0) - * detected. We use GetCodeNext. - */ - do { - if (DGifGetCodeNext(GifFile, &Dummy) == - GIF_ERROR) { - return GIF_ERROR; - } - } while (Dummy != NULL); + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + if (Private->PixelCount == 0) { + GifFile->Error = D_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + Private->PixelCount --; + + if (DGifDecompressLine(GifFile, &Pixel, 1) == GIF_OK) { + if (Private->PixelCount == 0) { + /* We probably won't be called any more, so let's clean + * up everything before we return: need to flush out all + * the rest of image until an empty block (size 0) + * detected. We use GetCodeNext. + */ + do { + if (DGifGetCodeNext(GifFile, &Dummy) == + GIF_ERROR) { + return GIF_ERROR; + } + } while (Dummy != NULL); + } + return GIF_OK; + } else { + return GIF_ERROR; } - return GIF_OK; - } else { - return GIF_ERROR; - } } /****************************************************************************** @@ -589,26 +591,26 @@ int DGifGetPixel(GifFileType *GifFile, GifPixelType Pixel) { ******************************************************************************/ int DGifGetExtension(GifFileType *GifFile, int *ExtCode, GifByteType **Extension) { - GifByteType Buf; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - // fprintf(stderr, "### -> DGifGetExtension:\n"); - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - GifFile->Error = D_GIF_ERR_NOT_READABLE; - return GIF_ERROR; - } + // fprintf(stderr, "### -> DGifGetExtension:\n"); + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } - /* coverity[check_return] */ - if (InternalRead(GifFile, &Buf, 1) != 1) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; - } - *ExtCode = Buf; - // fprintf(stderr, "### <- DGifGetExtension: %02x, about to call - // next\n", Buf); + /* coverity[check_return] */ + if (InternalRead(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + *ExtCode = Buf; + // fprintf(stderr, "### <- DGifGetExtension: %02x, about to call + // next\n", Buf); - return DGifGetExtensionNext(GifFile, Extension); + return DGifGetExtensionNext(GifFile, Extension); } /****************************************************************************** @@ -617,31 +619,31 @@ int DGifGetExtension(GifFileType *GifFile, int *ExtCode, The Extension should NOT be freed by the user (not dynamically allocated). ******************************************************************************/ int DGifGetExtensionNext(GifFileType *GifFile, GifByteType **Extension) { - GifByteType Buf; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - // fprintf(stderr, "### -> DGifGetExtensionNext\n"); - if (InternalRead(GifFile, &Buf, 1) != 1) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; - } - // fprintf(stderr, "### DGifGetExtensionNext sees %d\n", Buf); - - if (Buf > 0) { - *Extension = Private->Buf; /* Use private unused buffer. */ - (*Extension)[0] = - Buf; /* Pascal strings notation (pos. 0 is len.). */ - /* coverity[tainted_data,check_return] */ - if (InternalRead(GifFile, &((*Extension)[1]), Buf) != Buf) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; + // fprintf(stderr, "### -> DGifGetExtensionNext\n"); + if (InternalRead(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; } - } else { - *Extension = NULL; - } - // fprintf(stderr, "### <- DGifGetExtensionNext: %p\n", Extension); + // fprintf(stderr, "### DGifGetExtensionNext sees %d\n", Buf); - return GIF_OK; + if (Buf > 0) { + *Extension = Private->Buf; /* Use private unused buffer. */ + (*Extension)[0] = + Buf; /* Pascal strings notation (pos. 0 is len.). */ + /* coverity[tainted_data,check_return] */ + if (InternalRead(GifFile, &((*Extension)[1]), Buf) != Buf) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + } else { + *Extension = NULL; + } + // fprintf(stderr, "### <- DGifGetExtensionNext: %p\n", Extension); + + return GIF_OK; } /****************************************************************************** @@ -651,21 +653,21 @@ int DGifGetExtensionNext(GifFileType *GifFile, GifByteType **Extension) { int DGifExtensionToGCB(const size_t GifExtensionLength, const GifByteType *GifExtension, GraphicsControlBlock *GCB) { - if (GifExtensionLength != 4) { - return GIF_ERROR; - } + if (GifExtensionLength != 4) { + return GIF_ERROR; + } - GCB->DisposalMode = (GifExtension[0] >> 2) & 0x07; - GCB->UserInputFlag = (GifExtension[0] & 0x02) != 0; - GCB->DelayTime = - UNSIGNED_LITTLE_ENDIAN(GifExtension[1], GifExtension[2]); - if (GifExtension[0] & 0x01) { - GCB->TransparentColor = (int)GifExtension[3]; - } else { - GCB->TransparentColor = NO_TRANSPARENT_COLOR; - } + GCB->DisposalMode = (GifExtension[0] >> 2) & 0x07; + GCB->UserInputFlag = (GifExtension[0] & 0x02) != 0; + GCB->DelayTime = + UNSIGNED_LITTLE_ENDIAN(GifExtension[1], GifExtension[2]); + if (GifExtension[0] & 0x01) { + GCB->TransparentColor = (int)GifExtension[3]; + } else { + GCB->TransparentColor = NO_TRANSPARENT_COLOR; + } - return GIF_OK; + return GIF_OK; } /****************************************************************************** @@ -674,101 +676,101 @@ int DGifExtensionToGCB(const size_t GifExtensionLength, int DGifSavedExtensionToGCB(GifFileType *GifFile, int ImageIndex, GraphicsControlBlock *GCB) { - int i; + int i; - if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) { - return GIF_ERROR; - } - - GCB->DisposalMode = DISPOSAL_UNSPECIFIED; - GCB->UserInputFlag = false; - GCB->DelayTime = 0; - GCB->TransparentColor = NO_TRANSPARENT_COLOR; - - for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; - i++) { - ExtensionBlock *ep = - &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i]; - if (ep->Function == GRAPHICS_EXT_FUNC_CODE) { - return DGifExtensionToGCB(ep->ByteCount, ep->Bytes, - GCB); + if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) { + return GIF_ERROR; } - } - return GIF_ERROR; + GCB->DisposalMode = DISPOSAL_UNSPECIFIED; + GCB->UserInputFlag = false; + GCB->DelayTime = 0; + GCB->TransparentColor = NO_TRANSPARENT_COLOR; + + for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; + i++) { + ExtensionBlock *ep = + &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i]; + if (ep->Function == GRAPHICS_EXT_FUNC_CODE) { + return DGifExtensionToGCB(ep->ByteCount, ep->Bytes, + GCB); + } + } + + return GIF_ERROR; } /****************************************************************************** This routine should be called last, to close the GIF file. ******************************************************************************/ int DGifCloseFile(GifFileType *GifFile, int *ErrorCode) { - GifFilePrivateType *Private; + GifFilePrivateType *Private; - if (GifFile == NULL || GifFile->Private == NULL) { - return GIF_ERROR; - } - - if (GifFile->Image.ColorMap) { - GifFreeMapObject(GifFile->Image.ColorMap); - GifFile->Image.ColorMap = NULL; - } - - if (GifFile->SColorMap) { - GifFreeMapObject(GifFile->SColorMap); - GifFile->SColorMap = NULL; - } - - if (GifFile->SavedImages) { - GifFreeSavedImages(GifFile); - GifFile->SavedImages = NULL; - } - - GifFreeExtensions(&GifFile->ExtensionBlockCount, - &GifFile->ExtensionBlocks); - - Private = (GifFilePrivateType *)GifFile->Private; - - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - if (ErrorCode != NULL) { - *ErrorCode = D_GIF_ERR_NOT_READABLE; + if (GifFile == NULL || GifFile->Private == NULL) { + return GIF_ERROR; } + + if (GifFile->Image.ColorMap) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + + if (GifFile->SColorMap) { + GifFreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + } + + if (GifFile->SavedImages) { + GifFreeSavedImages(GifFile); + GifFile->SavedImages = NULL; + } + + GifFreeExtensions(&GifFile->ExtensionBlockCount, + &GifFile->ExtensionBlocks); + + Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + if (ErrorCode != NULL) { + *ErrorCode = D_GIF_ERR_NOT_READABLE; + } + free((char *)GifFile->Private); + free(GifFile); + return GIF_ERROR; + } + + if (Private->File && (fclose(Private->File) != 0)) { + if (ErrorCode != NULL) { + *ErrorCode = D_GIF_ERR_CLOSE_FAILED; + } + free((char *)GifFile->Private); + free(GifFile); + return GIF_ERROR; + } + free((char *)GifFile->Private); free(GifFile); - return GIF_ERROR; - } - - if (Private->File && (fclose(Private->File) != 0)) { if (ErrorCode != NULL) { - *ErrorCode = D_GIF_ERR_CLOSE_FAILED; + *ErrorCode = D_GIF_SUCCEEDED; } - free((char *)GifFile->Private); - free(GifFile); - return GIF_ERROR; - } - - free((char *)GifFile->Private); - free(GifFile); - if (ErrorCode != NULL) { - *ErrorCode = D_GIF_SUCCEEDED; - } - return GIF_OK; + return GIF_OK; } /****************************************************************************** Get 2 bytes (word) from the given file: ******************************************************************************/ static int DGifGetWord(GifFileType *GifFile, GifWord *Word) { - unsigned char c[2]; + unsigned char c[2]; - /* coverity[check_return] */ - if (InternalRead(GifFile, c, 2) != 2) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; - } + /* coverity[check_return] */ + if (InternalRead(GifFile, c, 2) != 2) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } - *Word = (GifWord)UNSIGNED_LITTLE_ENDIAN(c[0], c[1]); - return GIF_OK; + *Word = (GifWord)UNSIGNED_LITTLE_ENDIAN(c[0], c[1]); + return GIF_OK; } /****************************************************************************** @@ -779,17 +781,17 @@ static int DGifGetWord(GifFileType *GifFile, GifWord *Word) { The block should NOT be freed by the user (not dynamically allocated). ******************************************************************************/ int DGifGetCode(GifFileType *GifFile, int *CodeSize, GifByteType **CodeBlock) { - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - GifFile->Error = D_GIF_ERR_NOT_READABLE; - return GIF_ERROR; - } + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } - *CodeSize = Private->BitsPerPixel; + *CodeSize = Private->BitsPerPixel; - return DGifGetCodeNext(GifFile, CodeBlock); + return DGifGetCodeNext(GifFile, CodeBlock); } /****************************************************************************** @@ -798,78 +800,78 @@ int DGifGetCode(GifFileType *GifFile, int *CodeSize, GifByteType **CodeBlock) { The block should NOT be freed by the user (not dynamically allocated). ******************************************************************************/ int DGifGetCodeNext(GifFileType *GifFile, GifByteType **CodeBlock) { - GifByteType Buf; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - /* coverity[tainted_data_argument] */ - /* coverity[check_return] */ - if (InternalRead(GifFile, &Buf, 1) != 1) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; - } - - /* coverity[lower_bounds] */ - if (Buf > 0) { - *CodeBlock = Private->Buf; /* Use private unused buffer. */ - (*CodeBlock)[0] = - Buf; /* Pascal strings notation (pos. 0 is len.). */ - /* coverity[tainted_data] */ - if (InternalRead(GifFile, &((*CodeBlock)[1]), Buf) != Buf) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; + /* coverity[tainted_data_argument] */ + /* coverity[check_return] */ + if (InternalRead(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; } - } else { - *CodeBlock = NULL; - Private->Buf[0] = 0; /* Make sure the buffer is empty! */ - Private->PixelCount = - 0; /* And local info. indicate image read. */ - } - return GIF_OK; + /* coverity[lower_bounds] */ + if (Buf > 0) { + *CodeBlock = Private->Buf; /* Use private unused buffer. */ + (*CodeBlock)[0] = + Buf; /* Pascal strings notation (pos. 0 is len.). */ + /* coverity[tainted_data] */ + if (InternalRead(GifFile, &((*CodeBlock)[1]), Buf) != Buf) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + } else { + *CodeBlock = NULL; + Private->Buf[0] = 0; /* Make sure the buffer is empty! */ + Private->PixelCount = + 0; /* And local info. indicate image read. */ + } + + return GIF_OK; } /****************************************************************************** Setup the LZ decompression for this image: ******************************************************************************/ static int DGifSetupDecompress(GifFileType *GifFile) { - int i, BitsPerPixel; - GifByteType CodeSize; - GifPrefixType *Prefix; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + int i, BitsPerPixel; + GifByteType CodeSize; + GifPrefixType *Prefix; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - /* coverity[check_return] */ - if (InternalRead(GifFile, &CodeSize, 1) < - 1) { /* Read Code size from file. */ - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; /* Failed to read Code size. */ - } - BitsPerPixel = CodeSize; + /* coverity[check_return] */ + if (InternalRead(GifFile, &CodeSize, 1) < + 1) { /* Read Code size from file. */ + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; /* Failed to read Code size. */ + } + BitsPerPixel = CodeSize; - /* this can only happen on a severely malformed GIF */ - if (BitsPerPixel > 8) { - GifFile->Error = - D_GIF_ERR_READ_FAILED; /* somewhat bogus error code */ - return GIF_ERROR; /* Failed to read Code size. */ - } + /* this can only happen on a severely malformed GIF */ + if (BitsPerPixel > 8) { + GifFile->Error = + D_GIF_ERR_READ_FAILED; /* somewhat bogus error code */ + return GIF_ERROR; /* Failed to read Code size. */ + } - Private->Buf[0] = 0; /* Input Buffer empty. */ - Private->BitsPerPixel = BitsPerPixel; - Private->ClearCode = (1 << BitsPerPixel); - Private->EOFCode = Private->ClearCode + 1; - Private->RunningCode = Private->EOFCode + 1; - Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */ - Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */ - Private->StackPtr = 0; /* No pixels on the pixel stack. */ - Private->LastCode = NO_SUCH_CODE; - Private->CrntShiftState = 0; /* No information in CrntShiftDWord. */ - Private->CrntShiftDWord = 0; + Private->Buf[0] = 0; /* Input Buffer empty. */ + Private->BitsPerPixel = BitsPerPixel; + Private->ClearCode = (1 << BitsPerPixel); + Private->EOFCode = Private->ClearCode + 1; + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */ + Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */ + Private->StackPtr = 0; /* No pixels on the pixel stack. */ + Private->LastCode = NO_SUCH_CODE; + Private->CrntShiftState = 0; /* No information in CrntShiftDWord. */ + Private->CrntShiftDWord = 0; - Prefix = Private->Prefix; - for (i = 0; i <= LZ_MAX_CODE; i++) { - Prefix[i] = NO_SUCH_CODE; - } + Prefix = Private->Prefix; + for (i = 0; i <= LZ_MAX_CODE; i++) { + Prefix[i] = NO_SUCH_CODE; + } - return GIF_OK; + return GIF_OK; } /****************************************************************************** @@ -880,147 +882,147 @@ static int DGifSetupDecompress(GifFileType *GifFile) { ******************************************************************************/ static int DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) { - int i = 0; - int j, CrntCode, EOFCode, ClearCode, CrntPrefix, LastCode, StackPtr; - GifByteType *Stack, *Suffix; - GifPrefixType *Prefix; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + int i = 0; + int j, CrntCode, EOFCode, ClearCode, CrntPrefix, LastCode, StackPtr; + GifByteType *Stack, *Suffix; + GifPrefixType *Prefix; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - StackPtr = Private->StackPtr; - Prefix = Private->Prefix; - Suffix = Private->Suffix; - Stack = Private->Stack; - EOFCode = Private->EOFCode; - ClearCode = Private->ClearCode; - LastCode = Private->LastCode; + StackPtr = Private->StackPtr; + Prefix = Private->Prefix; + Suffix = Private->Suffix; + Stack = Private->Stack; + EOFCode = Private->EOFCode; + ClearCode = Private->ClearCode; + LastCode = Private->LastCode; - if (StackPtr > LZ_MAX_CODE) { - return GIF_ERROR; - } - - if (StackPtr != 0) { - /* Let pop the stack off before continueing to read the GIF - * file: */ - while (StackPtr != 0 && i < LineLen) { - Line[i++] = Stack[--StackPtr]; - } - } - - while (i < LineLen) { /* Decode LineLen items. */ - if (DGifDecompressInput(GifFile, &CrntCode) == GIF_ERROR) { - return GIF_ERROR; + if (StackPtr > LZ_MAX_CODE) { + return GIF_ERROR; } - if (CrntCode == EOFCode) { - /* Note however that usually we will not be here as we - * will stop decoding as soon as we got all the pixel, - * or EOF code will not be read at all, and - * DGifGetLine/Pixel clean everything. */ - GifFile->Error = D_GIF_ERR_EOF_TOO_SOON; - return GIF_ERROR; - } else if (CrntCode == ClearCode) { - /* We need to start over again: */ - for (j = 0; j <= LZ_MAX_CODE; j++) { - Prefix[j] = NO_SUCH_CODE; - } - Private->RunningCode = Private->EOFCode + 1; - Private->RunningBits = Private->BitsPerPixel + 1; - Private->MaxCode1 = 1 << Private->RunningBits; - LastCode = Private->LastCode = NO_SUCH_CODE; - } else { - /* Its regular code - if in pixel range simply add it to - * output stream, otherwise trace to codes linked list - * until the prefix is in pixel range: */ - if (CrntCode < ClearCode) { - /* This is simple - its pixel scalar, so add it - * to output: */ - Line[i++] = CrntCode; - } else { - /* Its a code to needed to be traced: trace the - * linked list until the prefix is a pixel, - * while pushing the suffix pixels on our stack. - * If we done, pop the stack in reverse (thats - * what stack is good for!) order to output. */ - if (Prefix[CrntCode] == NO_SUCH_CODE) { - CrntPrefix = LastCode; - - /* Only allowed if CrntCode is exactly - * the running code: In that case - * CrntCode = XXXCode, CrntCode or the - * prefix code is last code and the - * suffix char is exactly the prefix of - * last code! */ - if (CrntCode == - Private->RunningCode - 2) { - Suffix[Private->RunningCode - - 2] = Stack[StackPtr++] = - DGifGetPrefixChar( - Prefix, LastCode, - ClearCode); - } else { - Suffix[Private->RunningCode - - 2] = Stack[StackPtr++] = - DGifGetPrefixChar( - Prefix, CrntCode, - ClearCode); - } - } else { - CrntPrefix = CrntCode; - } - - /* Now (if image is O.K.) we should not get a - * NO_SUCH_CODE during the trace. As we might - * loop forever, in case of defective image, we - * use StackPtr as loop counter and stop before - * overflowing Stack[]. */ - while (StackPtr < LZ_MAX_CODE && - CrntPrefix > ClearCode && - CrntPrefix <= LZ_MAX_CODE) { - Stack[StackPtr++] = Suffix[CrntPrefix]; - CrntPrefix = Prefix[CrntPrefix]; - } - if (StackPtr >= LZ_MAX_CODE || - CrntPrefix > LZ_MAX_CODE) { - GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; - return GIF_ERROR; - } - /* Push the last character on stack: */ - Stack[StackPtr++] = CrntPrefix; - - /* Now lets pop all the stack into output: */ + if (StackPtr != 0) { + /* Let pop the stack off before continueing to read the GIF + * file: */ while (StackPtr != 0 && i < LineLen) { - Line[i++] = Stack[--StackPtr]; + Line[i++] = Stack[--StackPtr]; } - } - if (LastCode != NO_SUCH_CODE && - Private->RunningCode - 2 < (LZ_MAX_CODE + 1) && - Prefix[Private->RunningCode - 2] == NO_SUCH_CODE) { - Prefix[Private->RunningCode - 2] = LastCode; - - if (CrntCode == Private->RunningCode - 2) { - /* Only allowed if CrntCode is exactly - * the running code: In that case - * CrntCode = XXXCode, CrntCode or the - * prefix code is last code and the - * suffix char is exactly the prefix of - * last code! */ - Suffix[Private->RunningCode - 2] = - DGifGetPrefixChar(Prefix, LastCode, - ClearCode); - } else { - Suffix[Private->RunningCode - 2] = - DGifGetPrefixChar(Prefix, CrntCode, - ClearCode); - } - } - LastCode = CrntCode; } - } - Private->LastCode = LastCode; - Private->StackPtr = StackPtr; + while (i < LineLen) { /* Decode LineLen items. */ + if (DGifDecompressInput(GifFile, &CrntCode) == GIF_ERROR) { + return GIF_ERROR; + } - return GIF_OK; + if (CrntCode == EOFCode) { + /* Note however that usually we will not be here as we + * will stop decoding as soon as we got all the pixel, + * or EOF code will not be read at all, and + * DGifGetLine/Pixel clean everything. */ + GifFile->Error = D_GIF_ERR_EOF_TOO_SOON; + return GIF_ERROR; + } else if (CrntCode == ClearCode) { + /* We need to start over again: */ + for (j = 0; j <= LZ_MAX_CODE; j++) { + Prefix[j] = NO_SUCH_CODE; + } + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = Private->BitsPerPixel + 1; + Private->MaxCode1 = 1 << Private->RunningBits; + LastCode = Private->LastCode = NO_SUCH_CODE; + } else { + /* Its regular code - if in pixel range simply add it to + * output stream, otherwise trace to codes linked list + * until the prefix is in pixel range: */ + if (CrntCode < ClearCode) { + /* This is simple - its pixel scalar, so add it + * to output: */ + Line[i++] = CrntCode; + } else { + /* Its a code to needed to be traced: trace the + * linked list until the prefix is a pixel, + * while pushing the suffix pixels on our stack. + * If we done, pop the stack in reverse (thats + * what stack is good for!) order to output. */ + if (Prefix[CrntCode] == NO_SUCH_CODE) { + CrntPrefix = LastCode; + + /* Only allowed if CrntCode is exactly + * the running code: In that case + * CrntCode = XXXCode, CrntCode or the + * prefix code is last code and the + * suffix char is exactly the prefix of + * last code! */ + if (CrntCode == + Private->RunningCode - 2) { + Suffix[Private->RunningCode - + 2] = Stack[StackPtr++] = + DGifGetPrefixChar( + Prefix, LastCode, + ClearCode); + } else { + Suffix[Private->RunningCode - + 2] = Stack[StackPtr++] = + DGifGetPrefixChar( + Prefix, CrntCode, + ClearCode); + } + } else { + CrntPrefix = CrntCode; + } + + /* Now (if image is O.K.) we should not get a + * NO_SUCH_CODE during the trace. As we might + * loop forever, in case of defective image, we + * use StackPtr as loop counter and stop before + * overflowing Stack[]. */ + while (StackPtr < LZ_MAX_CODE && + CrntPrefix > ClearCode && + CrntPrefix <= LZ_MAX_CODE) { + Stack[StackPtr++] = Suffix[CrntPrefix]; + CrntPrefix = Prefix[CrntPrefix]; + } + if (StackPtr >= LZ_MAX_CODE || + CrntPrefix > LZ_MAX_CODE) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + /* Push the last character on stack: */ + Stack[StackPtr++] = CrntPrefix; + + /* Now lets pop all the stack into output: */ + while (StackPtr != 0 && i < LineLen) { + Line[i++] = Stack[--StackPtr]; + } + } + if (LastCode != NO_SUCH_CODE && + Private->RunningCode - 2 < (LZ_MAX_CODE + 1) && + Prefix[Private->RunningCode - 2] == NO_SUCH_CODE) { + Prefix[Private->RunningCode - 2] = LastCode; + + if (CrntCode == Private->RunningCode - 2) { + /* Only allowed if CrntCode is exactly + * the running code: In that case + * CrntCode = XXXCode, CrntCode or the + * prefix code is last code and the + * suffix char is exactly the prefix of + * last code! */ + Suffix[Private->RunningCode - 2] = + DGifGetPrefixChar(Prefix, LastCode, + ClearCode); + } else { + Suffix[Private->RunningCode - 2] = + DGifGetPrefixChar(Prefix, CrntCode, + ClearCode); + } + } + LastCode = CrntCode; + } + } + + Private->LastCode = LastCode; + Private->StackPtr = StackPtr; + + return GIF_OK; } /****************************************************************************** @@ -1031,15 +1033,15 @@ static int DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, ******************************************************************************/ static int DGifGetPrefixChar(const GifPrefixType *Prefix, int Code, int ClearCode) { - int i = 0; + int i = 0; - while (Code > ClearCode && i++ <= LZ_MAX_CODE) { - if (Code > LZ_MAX_CODE) { - return NO_SUCH_CODE; + while (Code > ClearCode && i++ <= LZ_MAX_CODE) { + if (Code > LZ_MAX_CODE) { + return NO_SUCH_CODE; + } + Code = Prefix[Code]; } - Code = Prefix[Code]; - } - return Code; + return Code; } /****************************************************************************** @@ -1047,37 +1049,37 @@ static int DGifGetPrefixChar(const GifPrefixType *Prefix, int Code, (12bits), or to -1 if EOF code is returned. ******************************************************************************/ int DGifGetLZCodes(GifFileType *GifFile, int *Code) { - GifByteType *CodeBlock; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + GifByteType *CodeBlock; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - GifFile->Error = D_GIF_ERR_NOT_READABLE; - return GIF_ERROR; - } - - if (DGifDecompressInput(GifFile, Code) == GIF_ERROR) { - return GIF_ERROR; - } - - if (*Code == Private->EOFCode) { - /* Skip rest of codes (hopefully only NULL terminating block): - */ - do { - if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR) { + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; return GIF_ERROR; - } - } while (CodeBlock != NULL); + } - *Code = -1; - } else if (*Code == Private->ClearCode) { - /* We need to start over again: */ - Private->RunningCode = Private->EOFCode + 1; - Private->RunningBits = Private->BitsPerPixel + 1; - Private->MaxCode1 = 1 << Private->RunningBits; - } + if (DGifDecompressInput(GifFile, Code) == GIF_ERROR) { + return GIF_ERROR; + } - return GIF_OK; + if (*Code == Private->EOFCode) { + /* Skip rest of codes (hopefully only NULL terminating block): + */ + do { + if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR) { + return GIF_ERROR; + } + } while (CodeBlock != NULL); + + *Code = -1; + } else if (*Code == Private->ClearCode) { + /* We need to start over again: */ + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = Private->BitsPerPixel + 1; + Private->MaxCode1 = 1 << Private->RunningBits; + } + + return GIF_OK; } /****************************************************************************** @@ -1087,47 +1089,47 @@ int DGifGetLZCodes(GifFileType *GifFile, int *Code) { Returns GIF_OK if read successfully. ******************************************************************************/ static int DGifDecompressInput(GifFileType *GifFile, int *Code) { - static const unsigned short CodeMasks[] = { - 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, - 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff}; + static const unsigned short CodeMasks[] = { + 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, + 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff}; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - GifByteType NextByte; + GifByteType NextByte; - /* The image can't contain more than LZ_BITS per code. */ - if (Private->RunningBits > LZ_BITS) { - GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; - return GIF_ERROR; - } - - while (Private->CrntShiftState < Private->RunningBits) { - /* Needs to get more bytes from input stream for next code: */ - if (DGifBufferedInput(GifFile, Private->Buf, &NextByte) == - GIF_ERROR) { - return GIF_ERROR; + /* The image can't contain more than LZ_BITS per code. */ + if (Private->RunningBits > LZ_BITS) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; } - Private->CrntShiftDWord |= ((unsigned long)NextByte) - << Private->CrntShiftState; - Private->CrntShiftState += 8; - } - *Code = Private->CrntShiftDWord & CodeMasks[Private->RunningBits]; - Private->CrntShiftDWord >>= Private->RunningBits; - Private->CrntShiftState -= Private->RunningBits; + while (Private->CrntShiftState < Private->RunningBits) { + /* Needs to get more bytes from input stream for next code: */ + if (DGifBufferedInput(GifFile, Private->Buf, &NextByte) == + GIF_ERROR) { + return GIF_ERROR; + } + Private->CrntShiftDWord |= ((unsigned long)NextByte) + << Private->CrntShiftState; + Private->CrntShiftState += 8; + } + *Code = Private->CrntShiftDWord & CodeMasks[Private->RunningBits]; - /* If code cannot fit into RunningBits bits, must raise its size. Note - * however that codes above 4095 are used for special signaling. - * If we're using LZ_BITS bits already and we're at the max code, just - * keep using the table as it is, don't increment Private->RunningCode. - */ - if (Private->RunningCode < LZ_MAX_CODE + 2 && - ++Private->RunningCode > Private->MaxCode1 && - Private->RunningBits < LZ_BITS) { - Private->MaxCode1 <<= 1; - Private->RunningBits++; - } - return GIF_OK; + Private->CrntShiftDWord >>= Private->RunningBits; + Private->CrntShiftState -= Private->RunningBits; + + /* If code cannot fit into RunningBits bits, must raise its size. Note + * however that codes above 4095 are used for special signaling. + * If we're using LZ_BITS bits already and we're at the max code, just + * keep using the table as it is, don't increment Private->RunningCode. + */ + if (Private->RunningCode < LZ_MAX_CODE + 2 && + ++Private->RunningCode > Private->MaxCode1 && + Private->RunningBits < LZ_BITS) { + Private->MaxCode1 <<= 1; + Private->RunningBits++; + } + return GIF_OK; } /****************************************************************************** @@ -1138,34 +1140,34 @@ static int DGifDecompressInput(GifFileType *GifFile, int *Code) { ******************************************************************************/ static int DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, GifByteType *NextByte) { - if (Buf[0] == 0) { - /* Needs to read the next buffer - this one is empty: */ - /* coverity[check_return] */ - if (InternalRead(GifFile, Buf, 1) != 1) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; - } - /* There shouldn't be any empty data blocks here as the LZW spec - * says the LZW termination code should come first. Therefore - * we shouldn't be inside this routine at that point. - */ if (Buf[0] == 0) { - GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; - return GIF_ERROR; + /* Needs to read the next buffer - this one is empty: */ + /* coverity[check_return] */ + if (InternalRead(GifFile, Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + /* There shouldn't be any empty data blocks here as the LZW spec + * says the LZW termination code should come first. Therefore + * we shouldn't be inside this routine at that point. + */ + if (Buf[0] == 0) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + if (InternalRead(GifFile, &Buf[1], Buf[0]) != Buf[0]) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + *NextByte = Buf[1]; + Buf[1] = 2; /* We use now the second place as last char read! */ + Buf[0]--; + } else { + *NextByte = Buf[Buf[1]++]; + Buf[0]--; } - if (InternalRead(GifFile, &Buf[1], Buf[0]) != Buf[0]) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; - } - *NextByte = Buf[1]; - Buf[1] = 2; /* We use now the second place as last char read! */ - Buf[0]--; - } else { - *NextByte = Buf[Buf[1]++]; - Buf[0]--; - } - return GIF_OK; + return GIF_OK; } /****************************************************************************** @@ -1175,17 +1177,20 @@ static int DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, SavedImages may point to the spoilt image and null pointer buffers. *******************************************************************************/ void DGifDecreaseImageCounter(GifFileType *GifFile) { - GifFile->ImageCount--; - if (GifFile->SavedImages[GifFile->ImageCount].RasterBits != NULL) { - free(GifFile->SavedImages[GifFile->ImageCount].RasterBits); - } + GifFile->ImageCount--; + if (GifFile->SavedImages[GifFile->ImageCount].RasterBits != NULL) { + free(GifFile->SavedImages[GifFile->ImageCount].RasterBits); + } + if (GifFile->SavedImages[GifFile->ImageCount].ImageDesc.ColorMap != NULL) { + GifFreeMapObject(GifFile->SavedImages[GifFile->ImageCount].ImageDesc.ColorMap); + } - // Realloc array according to the new image counter. - SavedImage *correct_saved_images = (SavedImage *)reallocarray( - GifFile->SavedImages, GifFile->ImageCount, sizeof(SavedImage)); - if (correct_saved_images != NULL) { - GifFile->SavedImages = correct_saved_images; - } + // Realloc array according to the new image counter. + SavedImage *correct_saved_images = (SavedImage *)reallocarray( + GifFile->SavedImages, GifFile->ImageCount, sizeof(SavedImage)); + if (correct_saved_images != NULL) { + GifFile->SavedImages = correct_saved_images; + } } /****************************************************************************** @@ -1194,143 +1199,143 @@ void DGifDecreaseImageCounter(GifFileType *GifFile) { first to initialize I/O. Its inverse is EGifSpew(). *******************************************************************************/ int DGifSlurp(GifFileType *GifFile) { - size_t ImageSize; - GifRecordType RecordType; - SavedImage *sp; - GifByteType *ExtData; - int ExtFunction; + size_t ImageSize; + GifRecordType RecordType; + SavedImage *sp; + GifByteType *ExtData; + int ExtFunction; - GifFile->ExtensionBlocks = NULL; - GifFile->ExtensionBlockCount = 0; + GifFile->ExtensionBlocks = NULL; + GifFile->ExtensionBlockCount = 0; - do { - if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) { - return (GIF_ERROR); - } + do { + if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) { + return (GIF_ERROR); + } - switch (RecordType) { - case IMAGE_DESC_RECORD_TYPE: - if (DGifGetImageDesc(GifFile) == GIF_ERROR) { - return (GIF_ERROR); - } - - sp = &GifFile->SavedImages[GifFile->ImageCount - 1]; - /* Allocate memory for the image */ - if (sp->ImageDesc.Width <= 0 || - sp->ImageDesc.Height <= 0 || - sp->ImageDesc.Width > - (INT_MAX / sp->ImageDesc.Height)) { - DGifDecreaseImageCounter(GifFile); - return GIF_ERROR; - } - ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height; - - if (ImageSize > (SIZE_MAX / sizeof(GifPixelType))) { - DGifDecreaseImageCounter(GifFile); - return GIF_ERROR; - } - sp->RasterBits = (unsigned char *)reallocarray( - NULL, ImageSize, sizeof(GifPixelType)); - - if (sp->RasterBits == NULL) { - DGifDecreaseImageCounter(GifFile); - return GIF_ERROR; - } - - if (sp->ImageDesc.Interlace) { - int i, j; - /* - * The way an interlaced image should be read - - * offsets and jumps... - */ - static const int InterlacedOffset[] = {0, 4, 2, - 1}; - static const int InterlacedJumps[] = {8, 8, 4, - 2}; - /* Need to perform 4 passes on the image */ - for (i = 0; i < 4; i++) { - for (j = InterlacedOffset[i]; - j < sp->ImageDesc.Height; - j += InterlacedJumps[i]) { - if (DGifGetLine( - GifFile, - sp->RasterBits + - j * sp->ImageDesc - .Width, - sp->ImageDesc.Width) == - GIF_ERROR) { - DGifDecreaseImageCounter( - GifFile); - return GIF_ERROR; + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFile) == GIF_ERROR) { + return (GIF_ERROR); } - } + + sp = &GifFile->SavedImages[GifFile->ImageCount - 1]; + /* Allocate memory for the image */ + if (sp->ImageDesc.Width <= 0 || + sp->ImageDesc.Height <= 0 || + sp->ImageDesc.Width > + (INT_MAX / sp->ImageDesc.Height)) { + DGifDecreaseImageCounter(GifFile); + return GIF_ERROR; + } + ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height; + + if (ImageSize > (SIZE_MAX / sizeof(GifPixelType))) { + DGifDecreaseImageCounter(GifFile); + return GIF_ERROR; + } + sp->RasterBits = (unsigned char *)reallocarray( + NULL, ImageSize, sizeof(GifPixelType)); + + if (sp->RasterBits == NULL) { + DGifDecreaseImageCounter(GifFile); + return GIF_ERROR; + } + + if (sp->ImageDesc.Interlace) { + int i, j; + /* + * The way an interlaced image should be read - + * offsets and jumps... + */ + static const int InterlacedOffset[] = {0, 4, 2, + 1}; + static const int InterlacedJumps[] = {8, 8, 4, + 2}; + /* Need to perform 4 passes on the image */ + for (i = 0; i < 4; i++) { + for (j = InterlacedOffset[i]; + j < sp->ImageDesc.Height; + j += InterlacedJumps[i]) { + if (DGifGetLine( + GifFile, + sp->RasterBits + + j * sp->ImageDesc + .Width, + sp->ImageDesc.Width) == + GIF_ERROR) { + DGifDecreaseImageCounter( + GifFile); + return GIF_ERROR; + } + } + } + } else { + if (DGifGetLine(GifFile, sp->RasterBits, + ImageSize) == GIF_ERROR) { + DGifDecreaseImageCounter(GifFile); + return GIF_ERROR; + } + } + + if (GifFile->ExtensionBlocks) { + sp->ExtensionBlocks = GifFile->ExtensionBlocks; + sp->ExtensionBlockCount = + GifFile->ExtensionBlockCount; + + GifFile->ExtensionBlocks = NULL; + GifFile->ExtensionBlockCount = 0; + } + break; + + case EXTENSION_RECORD_TYPE: + if (DGifGetExtension(GifFile, &ExtFunction, &ExtData) == + GIF_ERROR) { + return (GIF_ERROR); + } + /* Create an extension block with our data */ + if (ExtData != NULL) { + if (GifAddExtensionBlock( + &GifFile->ExtensionBlockCount, + &GifFile->ExtensionBlocks, ExtFunction, + ExtData[0], &ExtData[1]) == GIF_ERROR) { + return (GIF_ERROR); + } + } + for (;;) { + if (DGifGetExtensionNext(GifFile, &ExtData) == + GIF_ERROR) { + return (GIF_ERROR); + } + if (ExtData == NULL) { + break; + } + /* Continue the extension block */ + if (GifAddExtensionBlock( + &GifFile->ExtensionBlockCount, + &GifFile->ExtensionBlocks, + CONTINUE_EXT_FUNC_CODE, ExtData[0], + &ExtData[1]) == GIF_ERROR) { + return (GIF_ERROR); + } + } + break; + + case TERMINATE_RECORD_TYPE: + break; + + default: /* Should be trapped by DGifGetRecordType */ + break; } - } else { - if (DGifGetLine(GifFile, sp->RasterBits, - ImageSize) == GIF_ERROR) { - DGifDecreaseImageCounter(GifFile); - return GIF_ERROR; - } - } + } while (RecordType != TERMINATE_RECORD_TYPE); - if (GifFile->ExtensionBlocks) { - sp->ExtensionBlocks = GifFile->ExtensionBlocks; - sp->ExtensionBlockCount = - GifFile->ExtensionBlockCount; - - GifFile->ExtensionBlocks = NULL; - GifFile->ExtensionBlockCount = 0; - } - break; - - case EXTENSION_RECORD_TYPE: - if (DGifGetExtension(GifFile, &ExtFunction, &ExtData) == - GIF_ERROR) { + /* Sanity check for corrupted file */ + if (GifFile->ImageCount == 0) { + GifFile->Error = D_GIF_ERR_NO_IMAG_DSCR; return (GIF_ERROR); - } - /* Create an extension block with our data */ - if (ExtData != NULL) { - if (GifAddExtensionBlock( - &GifFile->ExtensionBlockCount, - &GifFile->ExtensionBlocks, ExtFunction, - ExtData[0], &ExtData[1]) == GIF_ERROR) { - return (GIF_ERROR); - } - } - for (;;) { - if (DGifGetExtensionNext(GifFile, &ExtData) == - GIF_ERROR) { - return (GIF_ERROR); - } - if (ExtData == NULL) { - break; - } - /* Continue the extension block */ - if (GifAddExtensionBlock( - &GifFile->ExtensionBlockCount, - &GifFile->ExtensionBlocks, - CONTINUE_EXT_FUNC_CODE, ExtData[0], - &ExtData[1]) == GIF_ERROR) { - return (GIF_ERROR); - } - } - break; - - case TERMINATE_RECORD_TYPE: - break; - - default: /* Should be trapped by DGifGetRecordType */ - break; } - } while (RecordType != TERMINATE_RECORD_TYPE); - /* Sanity check for corrupted file */ - if (GifFile->ImageCount == 0) { - GifFile->Error = D_GIF_ERR_NO_IMAG_DSCR; - return (GIF_ERROR); - } - - return (GIF_OK); + return (GIF_OK); } /* end */ diff --git a/src/java.desktop/share/native/libsplashscreen/giflib/gif_err.c b/src/java.desktop/share/native/libsplashscreen/giflib/gif_err.c index 3b6785f7c63..a3cc03b8865 100644 --- a/src/java.desktop/share/native/libsplashscreen/giflib/gif_err.c +++ b/src/java.desktop/share/native/libsplashscreen/giflib/gif_err.c @@ -26,9 +26,9 @@ gif_err.c - handle error reporting for the GIF library. -SPDX-License-Identifier: MIT - ****************************************************************************/ +// SPDX-License-Identifier: MIT +// SPDX-File-Copyright-Txt: (C) Copyright 1989 Gershon Elber #include @@ -39,83 +39,83 @@ SPDX-License-Identifier: MIT Return a string description of the last GIF error *****************************************************************************/ const char *GifErrorString(int ErrorCode) { - const char *Err; + const char *Err; - switch (ErrorCode) { - case E_GIF_ERR_OPEN_FAILED: - Err = "Failed to open given file"; - break; - case E_GIF_ERR_WRITE_FAILED: - Err = "Failed to write to given file"; - break; - case E_GIF_ERR_HAS_SCRN_DSCR: - Err = "Screen descriptor has already been set"; - break; - case E_GIF_ERR_HAS_IMAG_DSCR: - Err = "Image descriptor is still active"; - break; - case E_GIF_ERR_NO_COLOR_MAP: - Err = "Neither global nor local color map"; - break; - case E_GIF_ERR_DATA_TOO_BIG: - Err = "Number of pixels bigger than width * height"; - break; - case E_GIF_ERR_NOT_ENOUGH_MEM: - Err = "Failed to allocate required memory"; - break; - case E_GIF_ERR_DISK_IS_FULL: - Err = "Write failed (disk full?)"; - break; - case E_GIF_ERR_CLOSE_FAILED: - Err = "Failed to close given file"; - break; - case E_GIF_ERR_NOT_WRITEABLE: - Err = "Given file was not opened for write"; - break; - case D_GIF_ERR_OPEN_FAILED: - Err = "Failed to open given file"; - break; - case D_GIF_ERR_READ_FAILED: - Err = "Failed to read from given file"; - break; - case D_GIF_ERR_NOT_GIF_FILE: - Err = "Data is not in GIF format"; - break; - case D_GIF_ERR_NO_SCRN_DSCR: - Err = "No screen descriptor detected"; - break; - case D_GIF_ERR_NO_IMAG_DSCR: - Err = "No Image Descriptor detected"; - break; - case D_GIF_ERR_NO_COLOR_MAP: - Err = "Neither global nor local color map"; - break; - case D_GIF_ERR_WRONG_RECORD: - Err = "Wrong record type detected"; - break; - case D_GIF_ERR_DATA_TOO_BIG: - Err = "Number of pixels bigger than width * height"; - break; - case D_GIF_ERR_NOT_ENOUGH_MEM: - Err = "Failed to allocate required memory"; - break; - case D_GIF_ERR_CLOSE_FAILED: - Err = "Failed to close given file"; - break; - case D_GIF_ERR_NOT_READABLE: - Err = "Given file was not opened for read"; - break; - case D_GIF_ERR_IMAGE_DEFECT: - Err = "Image is defective, decoding aborted"; - break; - case D_GIF_ERR_EOF_TOO_SOON: - Err = "Image EOF detected before image complete"; - break; - default: - Err = NULL; - break; - } - return Err; + switch (ErrorCode) { + case E_GIF_ERR_OPEN_FAILED: + Err = "Failed to open given file"; + break; + case E_GIF_ERR_WRITE_FAILED: + Err = "Failed to write to given file"; + break; + case E_GIF_ERR_HAS_SCRN_DSCR: + Err = "Screen descriptor has already been set"; + break; + case E_GIF_ERR_HAS_IMAG_DSCR: + Err = "Image descriptor is still active"; + break; + case E_GIF_ERR_NO_COLOR_MAP: + Err = "Neither global nor local color map"; + break; + case E_GIF_ERR_DATA_TOO_BIG: + Err = "Number of pixels bigger than width * height"; + break; + case E_GIF_ERR_NOT_ENOUGH_MEM: + Err = "Failed to allocate required memory"; + break; + case E_GIF_ERR_DISK_IS_FULL: + Err = "Write failed (disk full?)"; + break; + case E_GIF_ERR_CLOSE_FAILED: + Err = "Failed to close given file"; + break; + case E_GIF_ERR_NOT_WRITEABLE: + Err = "Given file was not opened for write"; + break; + case D_GIF_ERR_OPEN_FAILED: + Err = "Failed to open given file"; + break; + case D_GIF_ERR_READ_FAILED: + Err = "Failed to read from given file"; + break; + case D_GIF_ERR_NOT_GIF_FILE: + Err = "Data is not in GIF format"; + break; + case D_GIF_ERR_NO_SCRN_DSCR: + Err = "No screen descriptor detected"; + break; + case D_GIF_ERR_NO_IMAG_DSCR: + Err = "No Image Descriptor detected"; + break; + case D_GIF_ERR_NO_COLOR_MAP: + Err = "Neither global nor local color map"; + break; + case D_GIF_ERR_WRONG_RECORD: + Err = "Wrong record type detected"; + break; + case D_GIF_ERR_DATA_TOO_BIG: + Err = "Number of pixels bigger than width * height"; + break; + case D_GIF_ERR_NOT_ENOUGH_MEM: + Err = "Failed to allocate required memory"; + break; + case D_GIF_ERR_CLOSE_FAILED: + Err = "Failed to close given file"; + break; + case D_GIF_ERR_NOT_READABLE: + Err = "Given file was not opened for read"; + break; + case D_GIF_ERR_IMAGE_DEFECT: + Err = "Image is defective, decoding aborted"; + break; + case D_GIF_ERR_EOF_TOO_SOON: + Err = "Image EOF detected before image complete"; + break; + default: + Err = NULL; + break; + } + return Err; } /* end */ diff --git a/src/java.desktop/share/native/libsplashscreen/giflib/gif_hash.h b/src/java.desktop/share/native/libsplashscreen/giflib/gif_hash.h index bd00af64161..eb3cba3135f 100644 --- a/src/java.desktop/share/native/libsplashscreen/giflib/gif_hash.h +++ b/src/java.desktop/share/native/libsplashscreen/giflib/gif_hash.h @@ -26,9 +26,8 @@ gif_hash.h - magfic constants and declarations for GIF LZW -SPDX-License-Identifier: MIT - ******************************************************************************/ +// SPDX-License-Identifier: MIT #ifndef _GIF_HASH_H_ #define _GIF_HASH_H_ @@ -46,7 +45,7 @@ SPDX-License-Identifier: MIT /* The 32 bits of the long are divided into two parts for the key & code: */ /* 1. The code is 12 bits as our compression algorithm is limited to 12bits */ -/* 2. The key is 12 bits Prefix code + 8 bit new char or 20 bits. */ +/* 2. The key is 12 bits Prefix code + 8 bit new char or 20 bits. */ /* The key is the upper 20 bits. The code is the lower 12. */ #define HT_GET_KEY(l) (l >> 12) #define HT_GET_CODE(l) (l & 0x0FFF) @@ -54,7 +53,7 @@ SPDX-License-Identifier: MIT #define HT_PUT_CODE(l) (l & 0x0FFF) typedef struct GifHashTableType { - uint32_t HTable[HT_SIZE]; + uint32_t HTable[HT_SIZE]; } GifHashTableType; GifHashTableType *_InitHashTable(void); diff --git a/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib.h b/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib.h index 74a2e969c0d..64b33beefa7 100644 --- a/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib.h +++ b/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib.h @@ -37,8 +37,8 @@ SPDX-License-Identifier: MIT extern "C" { #endif /* __cplusplus */ -#define GIFLIB_MAJOR 5 -#define GIFLIB_MINOR 2 +#define GIFLIB_MAJOR 6 +#define GIFLIB_MINOR 1 #define GIFLIB_RELEASE 2 #define GIF_ERROR 0 @@ -60,26 +60,26 @@ typedef unsigned int GifPrefixType; typedef int GifWord; typedef struct GifColorType { - GifByteType Red, Green, Blue; + GifByteType Red, Green, Blue; } GifColorType; typedef struct ColorMapObject { - int ColorCount; - int BitsPerPixel; - bool SortFlag; - GifColorType *Colors; /* on malloc(3) heap */ + int ColorCount; + int BitsPerPixel; + bool SortFlag; + GifColorType *Colors; /* on malloc(3) heap */ } ColorMapObject; typedef struct GifImageDesc { - GifWord Left, Top, Width, Height; /* Current image dimensions. */ - bool Interlace; /* Sequential/Interlaced lines. */ - ColorMapObject *ColorMap; /* The local color map */ + GifWord Left, Top, Width, Height; /* Current image dimensions. */ + bool Interlace; /* Sequential/Interlaced lines. */ + ColorMapObject *ColorMap; /* The local color map */ } GifImageDesc; typedef struct ExtensionBlock { - int ByteCount; - GifByteType *Bytes; /* on malloc(3) heap */ - int Function; /* The block function code */ + int ByteCount; + GifByteType *Bytes; /* on malloc(3) heap */ + int Function; /* The block function code */ #define CONTINUE_EXT_FUNC_CODE 0x00 /* continuation subblock */ #define COMMENT_EXT_FUNC_CODE 0xfe /* comment */ #define GRAPHICS_EXT_FUNC_CODE 0xf9 /* graphics control (GIF89) */ @@ -88,36 +88,36 @@ typedef struct ExtensionBlock { } ExtensionBlock; typedef struct SavedImage { - GifImageDesc ImageDesc; - GifByteType *RasterBits; /* on malloc(3) heap */ - int ExtensionBlockCount; /* Count of extensions before image */ - ExtensionBlock *ExtensionBlocks; /* Extensions before image */ + GifImageDesc ImageDesc; + GifByteType *RasterBits; /* on malloc(3) heap */ + int ExtensionBlockCount; /* Count of extensions before image */ + ExtensionBlock *ExtensionBlocks; /* Extensions before image */ } SavedImage; typedef struct GifFileType { - GifWord SWidth, SHeight; /* Size of virtual canvas */ - GifWord SColorResolution; /* How many colors can we generate? */ - GifWord SBackGroundColor; /* Background color for virtual canvas */ - GifByteType AspectByte; /* Used to compute pixel aspect ratio */ - ColorMapObject *SColorMap; /* Global colormap, NULL if nonexistent. */ - int ImageCount; /* Number of current image (both APIs) */ - GifImageDesc Image; /* Current image (low-level API) */ - SavedImage *SavedImages; /* Image sequence (high-level API) */ - int ExtensionBlockCount; /* Count extensions past last image */ - ExtensionBlock *ExtensionBlocks; /* Extensions past last image */ - int Error; /* Last error condition reported */ - void *UserData; /* hook to attach user data (TVT) */ - void *Private; /* Don't mess with this! */ + GifWord SWidth, SHeight; /* Size of virtual canvas */ + GifWord SColorResolution; /* How many colors can we generate? */ + GifWord SBackGroundColor; /* Background color for virtual canvas */ + GifByteType AspectByte; /* Used to compute pixel aspect ratio */ + ColorMapObject *SColorMap; /* Global colormap, NULL if nonexistent. */ + int ImageCount; /* Number of current image (both APIs) */ + GifImageDesc Image; /* Current image (low-level API) */ + SavedImage *SavedImages; /* Image sequence (high-level API) */ + int ExtensionBlockCount; /* Count extensions past last image */ + ExtensionBlock *ExtensionBlocks; /* Extensions past last image */ + int Error; /* Last error condition reported */ + void *UserData; /* hook to attach user data (TVT) */ + void *Private; /* Don't mess with this! */ } GifFileType; #define GIF_ASPECT_RATIO(n) ((n) + 15.0 / 64.0) typedef enum { - UNDEFINED_RECORD_TYPE, - SCREEN_DESC_RECORD_TYPE, - IMAGE_DESC_RECORD_TYPE, /* Begin with ',' */ - EXTENSION_RECORD_TYPE, /* Begin with '!' */ - TERMINATE_RECORD_TYPE /* Begin with ';' */ + UNDEFINED_RECORD_TYPE, + SCREEN_DESC_RECORD_TYPE, + IMAGE_DESC_RECORD_TYPE, /* Begin with ',' */ + EXTENSION_RECORD_TYPE, /* Begin with '!' */ + TERMINATE_RECORD_TYPE /* Begin with ';' */ } GifRecordType; /* func type to read gif data from arbitrary sources (TVT) */ @@ -133,14 +133,14 @@ typedef int (*OutputFunc)(GifFileType *, const GifByteType *, int); ******************************************************************************/ typedef struct GraphicsControlBlock { - int DisposalMode; + int DisposalMode; #define DISPOSAL_UNSPECIFIED 0 /* No disposal specified. */ #define DISPOSE_DO_NOT 1 /* Leave image in place */ #define DISPOSE_BACKGROUND 2 /* Set area too background color */ #define DISPOSE_PREVIOUS 3 /* Restore to previous content */ - bool UserInputFlag; /* User confirmation required before disposal */ - int DelayTime; /* pre-display delay in 0.01sec units */ - int TransparentColor; /* Palette index for transparency, -1 if none */ + bool UserInputFlag; /* User confirmation required before disposal */ + int DelayTime; /* pre-display delay in 0.01sec units */ + int TransparentColor; /* Palette index for transparency, -1 if none */ #define NO_TRANSPARENT_COLOR -1 } GraphicsControlBlock; @@ -153,21 +153,21 @@ GifFileType *EGifOpenFileName(const char *GifFileName, const bool GifTestExistence, int *Error); GifFileType *EGifOpenFileHandle(const int GifFileHandle, int *Error); GifFileType *EGifOpen(void *userPtr, OutputFunc writeFunc, int *Error); -int EGifSpew(GifFileType *GifFile); +int EGifSpew(GifFileType *GifFile, int *ErrorCode); const char *EGifGetGifVersion(GifFileType *GifFile); /* new in 5.x */ int EGifCloseFile(GifFileType *GifFile, int *ErrorCode); #define E_GIF_SUCCEEDED 0 -#define E_GIF_ERR_OPEN_FAILED 1 /* And EGif possible errors. */ -#define E_GIF_ERR_WRITE_FAILED 2 -#define E_GIF_ERR_HAS_SCRN_DSCR 3 -#define E_GIF_ERR_HAS_IMAG_DSCR 4 -#define E_GIF_ERR_NO_COLOR_MAP 5 -#define E_GIF_ERR_DATA_TOO_BIG 6 -#define E_GIF_ERR_NOT_ENOUGH_MEM 7 -#define E_GIF_ERR_DISK_IS_FULL 8 -#define E_GIF_ERR_CLOSE_FAILED 9 -#define E_GIF_ERR_NOT_WRITEABLE 10 +#define E_GIF_ERR_OPEN_FAILED 201 /* And EGif possible errors. */ +#define E_GIF_ERR_WRITE_FAILED 202 +#define E_GIF_ERR_HAS_SCRN_DSCR 203 +#define E_GIF_ERR_HAS_IMAG_DSCR 204 +#define E_GIF_ERR_NO_COLOR_MAP 205 +#define E_GIF_ERR_DATA_TOO_BIG 206 +#define E_GIF_ERR_NOT_ENOUGH_MEM 207 +#define E_GIF_ERR_DISK_IS_FULL 208 +#define E_GIF_ERR_CLOSE_FAILED 209 +#define E_GIF_ERR_NOT_WRITEABLE 210 /* These are legacy. You probably do not want to call them directly */ int EGifPutScreenDesc(GifFileType *GifFile, const int GifWidth, diff --git a/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib_private.h b/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib_private.h index f905e0d7b48..079d05898b4 100644 --- a/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib_private.h +++ b/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib_private.h @@ -60,30 +60,30 @@ SPDX-License-Identifier: MIT #define IS_WRITEABLE(Private) (Private->FileState & FILE_STATE_WRITE) typedef struct GifFilePrivateType { - GifWord FileState, FileHandle, /* Where all this data goes to! */ - BitsPerPixel, /* Bits per pixel (Codes uses at least this + 1). */ - ClearCode, /* The CLEAR LZ code. */ - EOFCode, /* The EOF LZ code. */ - RunningCode, /* The next code algorithm can generate. */ - RunningBits, /* The number of bits required to represent - RunningCode. */ - MaxCode1, /* 1 bigger than max. possible code, in RunningBits bits. - */ - LastCode, /* The code before the current code. */ - CrntCode, /* Current algorithm code. */ - StackPtr, /* For character stack (see below). */ - CrntShiftState; /* Number of bits in CrntShiftDWord. */ - unsigned long CrntShiftDWord; /* For bytes decomposition into codes. */ - unsigned long PixelCount; /* Number of pixels in image. */ - FILE *File; /* File as stream. */ - InputFunc Read; /* function to read gif input (TVT) */ - OutputFunc Write; /* function to write gif output (MRB) */ - GifByteType Buf[256]; /* Compressed input is buffered here. */ - GifByteType Stack[LZ_MAX_CODE]; /* Decoded pixels are stacked here. */ - GifByteType Suffix[LZ_MAX_CODE + 1]; /* So we can trace the codes. */ - GifPrefixType Prefix[LZ_MAX_CODE + 1]; - GifHashTableType *HashTable; - bool gif89; + GifWord FileState, FileHandle, /* Where all this data goes to! */ + BitsPerPixel, /* Bits per pixel (Codes uses at least this + 1). */ + ClearCode, /* The CLEAR LZ code. */ + EOFCode, /* The EOF LZ code. */ + RunningCode, /* The next code algorithm can generate. */ + RunningBits, /* The number of bits required to represent + RunningCode. */ + MaxCode1, /* 1 bigger than max. possible code, in RunningBits bits. + */ + LastCode, /* The code before the current code. */ + CrntCode, /* Current algorithm code. */ + StackPtr, /* For character stack (see below). */ + CrntShiftState; /* Number of bits in CrntShiftDWord. */ + unsigned long CrntShiftDWord; /* For bytes decomposition into codes. */ + unsigned long PixelCount; /* Number of pixels in image. */ + FILE *File; /* File as stream. */ + InputFunc Read; /* function to read gif input (TVT) */ + OutputFunc Write; /* function to write gif output (MRB) */ + GifByteType Buf[256]; /* Compressed input is buffered here. */ + GifByteType Stack[LZ_MAX_CODE]; /* Decoded pixels are stacked here. */ + GifByteType Suffix[LZ_MAX_CODE + 1]; /* So we can trace the codes. */ + GifPrefixType Prefix[LZ_MAX_CODE + 1]; + GifHashTableType *HashTable; + bool gif89; } GifFilePrivateType; #ifndef HAVE_REALLOCARRAY diff --git a/src/java.desktop/share/native/libsplashscreen/giflib/gifalloc.c b/src/java.desktop/share/native/libsplashscreen/giflib/gifalloc.c index 5aef3044558..25e03914496 100644 --- a/src/java.desktop/share/native/libsplashscreen/giflib/gifalloc.c +++ b/src/java.desktop/share/native/libsplashscreen/giflib/gifalloc.c @@ -26,9 +26,9 @@ GIF construction tools -SPDX-License-Identifier: MIT - ****************************************************************************/ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: Copyright (C) Eric S. Raymond #include #include @@ -45,14 +45,14 @@ SPDX-License-Identifier: MIT /* return smallest bitfield size n will fit in */ int GifBitSize(int n) { - register int i; + register int i; - for (i = 1; i <= 8; i++) { - if ((1 << i) >= n) { - break; + for (i = 1; i <= 8; i++) { + if ((1 << i) >= n) { + break; + } } - } - return (i); + return (i); } /****************************************************************************** @@ -64,64 +64,64 @@ int GifBitSize(int n) { * ColorMap if that pointer is non-NULL. */ ColorMapObject *GifMakeMapObject(int ColorCount, const GifColorType *ColorMap) { - ColorMapObject *Object; + ColorMapObject *Object; - /*** FIXME: Our ColorCount has to be a power of two. Is it necessary to - * make the user know that or should we automatically round up instead? - */ - if (ColorCount != (1 << GifBitSize(ColorCount))) { - return ((ColorMapObject *)NULL); - } + /*** FIXME: Our ColorCount has to be a power of two. Is it necessary to + * make the user know that or should we automatically round up instead? + */ + if (ColorCount != (1 << GifBitSize(ColorCount))) { + return ((ColorMapObject *)NULL); + } - Object = (ColorMapObject *)malloc(sizeof(ColorMapObject)); - if (Object == (ColorMapObject *)NULL) { - return ((ColorMapObject *)NULL); - } + Object = (ColorMapObject *)malloc(sizeof(ColorMapObject)); + if (Object == (ColorMapObject *)NULL) { + return ((ColorMapObject *)NULL); + } - Object->Colors = - (GifColorType *)calloc(ColorCount, sizeof(GifColorType)); - if (Object->Colors == (GifColorType *)NULL) { - free(Object); - return ((ColorMapObject *)NULL); - } + Object->Colors = + (GifColorType *)calloc(ColorCount, sizeof(GifColorType)); + if (Object->Colors == (GifColorType *)NULL) { + free(Object); + return ((ColorMapObject *)NULL); + } - Object->ColorCount = ColorCount; - Object->BitsPerPixel = GifBitSize(ColorCount); - Object->SortFlag = false; + Object->ColorCount = ColorCount; + Object->BitsPerPixel = GifBitSize(ColorCount); + Object->SortFlag = false; - if (ColorMap != NULL) { - memcpy((char *)Object->Colors, (char *)ColorMap, - ColorCount * sizeof(GifColorType)); - } + if (ColorMap != NULL) { + memcpy((char *)Object->Colors, (char *)ColorMap, + ColorCount * sizeof(GifColorType)); + } - return (Object); + return (Object); } /******************************************************************************* Free a color map object *******************************************************************************/ void GifFreeMapObject(ColorMapObject *Object) { - if (Object != NULL) { - (void)free(Object->Colors); - (void)free(Object); - } + if (Object != NULL) { + (void)free(Object->Colors); + (void)free(Object); + } } #ifdef DEBUG void DumpColorMap(ColorMapObject *Object, FILE *fp) { - if (Object != NULL) { - int i, j, Len = Object->ColorCount; + if (Object != NULL) { + int i, j, Len = Object->ColorCount; - for (i = 0; i < Len; i += 4) { - for (j = 0; j < 4 && j < Len; j++) { - (void)fprintf(fp, "%3d: %02x %02x %02x ", - i + j, Object->Colors[i + j].Red, - Object->Colors[i + j].Green, - Object->Colors[i + j].Blue); - } - (void)fprintf(fp, "\n"); + for (i = 0; i < Len; i += 4) { + for (j = 0; j < 4 && j < Len; j++) { + (void)fprintf(fp, "%3d: %02x %02x %02x ", + i + j, Object->Colors[i + j].Red, + Object->Colors[i + j].Green, + Object->Colors[i + j].Blue); + } + (void)fprintf(fp, "\n"); + } } - } } #endif /* DEBUG */ @@ -135,112 +135,112 @@ void DumpColorMap(ColorMapObject *Object, FILE *fp) { ColorMapObject *GifUnionColorMap(const ColorMapObject *ColorIn1, const ColorMapObject *ColorIn2, GifPixelType ColorTransIn2[]) { - int i, j, CrntSlot, RoundUpTo, NewGifBitSize; - ColorMapObject *ColorUnion; - - /* - * We don't worry about duplicates within either color map; if - * the caller wants to resolve those, he can perform unions - * with an empty color map. - */ - - /* Allocate table which will hold the result for sure. */ - ColorUnion = GifMakeMapObject( - MAX(ColorIn1->ColorCount, ColorIn2->ColorCount) * 2, NULL); - - if (ColorUnion == NULL) { - return (NULL); - } - - /* - * Copy ColorIn1 to ColorUnion. - */ - for (i = 0; i < ColorIn1->ColorCount; i++) { - ColorUnion->Colors[i] = ColorIn1->Colors[i]; - } - CrntSlot = ColorIn1->ColorCount; - - /* - * Potentially obnoxious hack: - * - * Back CrntSlot down past all contiguous {0, 0, 0} slots at the end - * of table 1. This is very useful if your display is limited to - * 16 colors. - */ - while (ColorIn1->Colors[CrntSlot - 1].Red == 0 && - ColorIn1->Colors[CrntSlot - 1].Green == 0 && - ColorIn1->Colors[CrntSlot - 1].Blue == 0) { - CrntSlot--; - } - - /* Copy ColorIn2 to ColorUnion (use old colors if they exist): */ - for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) { - /* Let's see if this color already exists: */ - for (j = 0; j < ColorIn1->ColorCount; j++) { - if (memcmp(&ColorIn1->Colors[j], &ColorIn2->Colors[i], - sizeof(GifColorType)) == 0) { - break; - } - } - - if (j < ColorIn1->ColorCount) { - ColorTransIn2[i] = j; /* color exists in Color1 */ - } else { - /* Color is new - copy it to a new slot: */ - ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i]; - ColorTransIn2[i] = CrntSlot++; - } - } - - if (CrntSlot > 256) { - GifFreeMapObject(ColorUnion); - return ((ColorMapObject *)NULL); - } - - NewGifBitSize = GifBitSize(CrntSlot); - RoundUpTo = (1 << NewGifBitSize); - - if (RoundUpTo != ColorUnion->ColorCount) { - register GifColorType *Map = ColorUnion->Colors; + int i, j, CrntSlot, RoundUpTo, NewGifBitSize; + ColorMapObject *ColorUnion; /* - * Zero out slots up to next power of 2. - * We know these slots exist because of the way ColorUnion's - * start dimension was computed. + * We don't worry about duplicates within either color map; if + * the caller wants to resolve those, he can perform unions + * with an empty color map. */ - for (j = CrntSlot; j < RoundUpTo; j++) { - Map[j].Red = Map[j].Green = Map[j].Blue = 0; + + /* Allocate table which will hold the result for sure. */ + ColorUnion = GifMakeMapObject( + MAX(ColorIn1->ColorCount, ColorIn2->ColorCount) * 2, NULL); + + if (ColorUnion == NULL) { + return (NULL); } - /* perhaps we can shrink the map? */ - if (RoundUpTo < ColorUnion->ColorCount) { - GifColorType *new_map = (GifColorType *)reallocarray( - Map, RoundUpTo, sizeof(GifColorType)); - if (new_map == NULL) { + /* + * Copy ColorIn1 to ColorUnion. + */ + for (i = 0; i < ColorIn1->ColorCount; i++) { + ColorUnion->Colors[i] = ColorIn1->Colors[i]; + } + CrntSlot = ColorIn1->ColorCount; + + /* + * Potentially obnoxious hack: + * + * Back CrntSlot down past all contiguous {0, 0, 0} slots at the end + * of table 1. This is very useful if your display is limited to + * 16 colors. + */ + while (ColorIn1->Colors[CrntSlot - 1].Red == 0 && + ColorIn1->Colors[CrntSlot - 1].Green == 0 && + ColorIn1->Colors[CrntSlot - 1].Blue == 0) { + CrntSlot--; + } + + /* Copy ColorIn2 to ColorUnion (use old colors if they exist): */ + for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) { + /* Let's see if this color already exists: */ + for (j = 0; j < ColorIn1->ColorCount; j++) { + if (memcmp(&ColorIn1->Colors[j], &ColorIn2->Colors[i], + sizeof(GifColorType)) == 0) { + break; + } + } + + if (j < ColorIn1->ColorCount) { + ColorTransIn2[i] = j; /* color exists in Color1 */ + } else { + /* Color is new - copy it to a new slot: */ + ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i]; + ColorTransIn2[i] = CrntSlot++; + } + } + + if (CrntSlot > 256) { GifFreeMapObject(ColorUnion); return ((ColorMapObject *)NULL); - } - ColorUnion->Colors = new_map; } - } - ColorUnion->ColorCount = RoundUpTo; - ColorUnion->BitsPerPixel = NewGifBitSize; + NewGifBitSize = GifBitSize(CrntSlot); + RoundUpTo = (1 << NewGifBitSize); - return (ColorUnion); + if (RoundUpTo != ColorUnion->ColorCount) { + register GifColorType *Map = ColorUnion->Colors; + + /* + * Zero out slots up to next power of 2. + * We know these slots exist because of the way ColorUnion's + * start dimension was computed. + */ + for (j = CrntSlot; j < RoundUpTo; j++) { + Map[j].Red = Map[j].Green = Map[j].Blue = 0; + } + + /* perhaps we can shrink the map? */ + if (RoundUpTo < ColorUnion->ColorCount) { + GifColorType *new_map = (GifColorType *)reallocarray( + Map, RoundUpTo, sizeof(GifColorType)); + if (new_map == NULL) { + GifFreeMapObject(ColorUnion); + return ((ColorMapObject *)NULL); + } + ColorUnion->Colors = new_map; + } + } + + ColorUnion->ColorCount = RoundUpTo; + ColorUnion->BitsPerPixel = NewGifBitSize; + + return (ColorUnion); } /******************************************************************************* Apply a given color translation to the raster bits of an image *******************************************************************************/ void GifApplyTranslation(SavedImage *Image, const GifPixelType Translation[]) { - register int i; - register int RasterSize = - Image->ImageDesc.Height * Image->ImageDesc.Width; + register int i; + register int RasterSize = + Image->ImageDesc.Height * Image->ImageDesc.Width; - for (i = 0; i < RasterSize; i++) { - Image->RasterBits[i] = Translation[Image->RasterBits[i]]; - } + for (i = 0; i < RasterSize; i++) { + Image->RasterBits[i] = Translation[Image->RasterBits[i]]; + } } /****************************************************************************** @@ -249,56 +249,56 @@ void GifApplyTranslation(SavedImage *Image, const GifPixelType Translation[]) { int GifAddExtensionBlock(int *ExtensionBlockCount, ExtensionBlock **ExtensionBlocks, int Function, unsigned int Len, unsigned char ExtData[]) { - ExtensionBlock *ep; + ExtensionBlock *ep; - if (*ExtensionBlocks == NULL) { - *ExtensionBlocks = - (ExtensionBlock *)malloc(sizeof(ExtensionBlock)); - } else { - ExtensionBlock *ep_new = (ExtensionBlock *)reallocarray( - *ExtensionBlocks, (*ExtensionBlockCount + 1), - sizeof(ExtensionBlock)); - if (ep_new == NULL) { - return (GIF_ERROR); + if (*ExtensionBlocks == NULL) { + *ExtensionBlocks = + (ExtensionBlock *)malloc(sizeof(ExtensionBlock)); + } else { + ExtensionBlock *ep_new = (ExtensionBlock *)reallocarray( + *ExtensionBlocks, (*ExtensionBlockCount + 1), + sizeof(ExtensionBlock)); + if (ep_new == NULL) { + return (GIF_ERROR); + } + *ExtensionBlocks = ep_new; } - *ExtensionBlocks = ep_new; - } - if (*ExtensionBlocks == NULL) { - return (GIF_ERROR); - } + if (*ExtensionBlocks == NULL) { + return (GIF_ERROR); + } - ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++]; + ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++]; - ep->Function = Function; - ep->ByteCount = Len; - ep->Bytes = (GifByteType *)malloc(ep->ByteCount); - if (ep->Bytes == NULL) { - return (GIF_ERROR); - } + ep->Function = Function; + ep->ByteCount = Len; + ep->Bytes = (GifByteType *)malloc(ep->ByteCount); + if (ep->Bytes == NULL) { + return (GIF_ERROR); + } - if (ExtData != NULL) { - memcpy(ep->Bytes, ExtData, Len); - } + if (ExtData != NULL) { + memcpy(ep->Bytes, ExtData, Len); + } - return (GIF_OK); + return (GIF_OK); } void GifFreeExtensions(int *ExtensionBlockCount, ExtensionBlock **ExtensionBlocks) { - ExtensionBlock *ep; + ExtensionBlock *ep; - if (*ExtensionBlocks == NULL) { - return; - } + if (*ExtensionBlocks == NULL) { + return; + } - for (ep = *ExtensionBlocks; - ep < (*ExtensionBlocks + *ExtensionBlockCount); ep++) { - (void)free((char *)ep->Bytes); - } - (void)free((char *)*ExtensionBlocks); - *ExtensionBlocks = NULL; - *ExtensionBlockCount = 0; + for (ep = *ExtensionBlocks; + ep < (*ExtensionBlocks + *ExtensionBlockCount); ep++) { + (void)free((char *)ep->Bytes); + } + (void)free((char *)*ExtensionBlocks); + *ExtensionBlocks = NULL; + *ExtensionBlockCount = 0; } /****************************************************************************** @@ -309,37 +309,37 @@ void GifFreeExtensions(int *ExtensionBlockCount, * Frees the last image in the GifFile->SavedImages array */ void FreeLastSavedImage(GifFileType *GifFile) { - SavedImage *sp; + SavedImage *sp; - if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) { - return; - } + if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) { + return; + } - /* Remove one SavedImage from the GifFile */ - GifFile->ImageCount--; - sp = &GifFile->SavedImages[GifFile->ImageCount]; + /* Remove one SavedImage from the GifFile */ + GifFile->ImageCount--; + sp = &GifFile->SavedImages[GifFile->ImageCount]; - /* Deallocate its Colormap */ - if (sp->ImageDesc.ColorMap != NULL) { - GifFreeMapObject(sp->ImageDesc.ColorMap); - sp->ImageDesc.ColorMap = NULL; - } + /* Deallocate its Colormap */ + if (sp->ImageDesc.ColorMap != NULL) { + GifFreeMapObject(sp->ImageDesc.ColorMap); + sp->ImageDesc.ColorMap = NULL; + } - /* Deallocate the image data */ - if (sp->RasterBits != NULL) { - free((char *)sp->RasterBits); - } + /* Deallocate the image data */ + if (sp->RasterBits != NULL) { + free((char *)sp->RasterBits); + } - /* Deallocate any extensions */ - GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks); + /* Deallocate any extensions */ + GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks); - /*** FIXME: We could realloc the GifFile->SavedImages structure but is - * there a point to it? Saves some memory but we'd have to do it every - * time. If this is used in GifFreeSavedImages then it would be - * inefficient (The whole array is going to be deallocated.) If we just - * use it when we want to free the last Image it's convenient to do it - * here. - */ + /*** FIXME: We could realloc the GifFile->SavedImages structure but is + * there a point to it? Saves some memory but we'd have to do it every + * time. If this is used in GifFreeSavedImages then it would be + * inefficient (The whole array is going to be deallocated.) If we just + * use it when we want to free the last Image it's convenient to do it + * here. + */ } /* @@ -347,103 +347,129 @@ void FreeLastSavedImage(GifFileType *GifFile) { */ SavedImage *GifMakeSavedImage(GifFileType *GifFile, const SavedImage *CopyFrom) { - // cppcheck-suppress ctunullpointer - if (GifFile->SavedImages == NULL) { - GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage)); - } else { - SavedImage *newSavedImages = (SavedImage *)reallocarray( - GifFile->SavedImages, (GifFile->ImageCount + 1), - sizeof(SavedImage)); - if (newSavedImages == NULL) { - return ((SavedImage *)NULL); - } - GifFile->SavedImages = newSavedImages; - } - if (GifFile->SavedImages == NULL) { - return ((SavedImage *)NULL); - } else { - SavedImage *sp = &GifFile->SavedImages[GifFile->ImageCount++]; - - if (CopyFrom != NULL) { - memcpy((char *)sp, CopyFrom, sizeof(SavedImage)); - - /* - * Make our own allocated copies of the heap fields in - * the copied record. This guards against potential - * aliasing problems. - */ - - /* first, the local color map */ - if (CopyFrom->ImageDesc.ColorMap != NULL) { - sp->ImageDesc.ColorMap = GifMakeMapObject( - CopyFrom->ImageDesc.ColorMap->ColorCount, - CopyFrom->ImageDesc.ColorMap->Colors); - if (sp->ImageDesc.ColorMap == NULL) { - FreeLastSavedImage(GifFile); - return (SavedImage *)(NULL); - } - } - - /* next, the raster */ - sp->RasterBits = (unsigned char *)reallocarray( - NULL, - (CopyFrom->ImageDesc.Height * - CopyFrom->ImageDesc.Width), - sizeof(GifPixelType)); - if (sp->RasterBits == NULL) { - FreeLastSavedImage(GifFile); - return (SavedImage *)(NULL); - } - memcpy(sp->RasterBits, CopyFrom->RasterBits, - sizeof(GifPixelType) * - CopyFrom->ImageDesc.Height * - CopyFrom->ImageDesc.Width); - - /* finally, the extension blocks */ - if (CopyFrom->ExtensionBlocks != NULL) { - sp->ExtensionBlocks = - (ExtensionBlock *)reallocarray( - NULL, CopyFrom->ExtensionBlockCount, - sizeof(ExtensionBlock)); - if (sp->ExtensionBlocks == NULL) { - FreeLastSavedImage(GifFile); - return (SavedImage *)(NULL); - } - memcpy(sp->ExtensionBlocks, - CopyFrom->ExtensionBlocks, - sizeof(ExtensionBlock) * - CopyFrom->ExtensionBlockCount); - } + // cppcheck-suppress ctunullpointer + if (GifFile->SavedImages == NULL) { + GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage)); } else { - memset((char *)sp, '\0', sizeof(SavedImage)); + SavedImage *newSavedImages = (SavedImage *)reallocarray( + GifFile->SavedImages, (GifFile->ImageCount + 1), + sizeof(SavedImage)); + if (newSavedImages == NULL) { + return ((SavedImage *)NULL); + } + GifFile->SavedImages = newSavedImages; } + if (GifFile->SavedImages == NULL) { + return ((SavedImage *)NULL); + } else { + SavedImage *sp = &GifFile->SavedImages[GifFile->ImageCount++]; - return (sp); - } + if (CopyFrom != NULL) { + memcpy((char *)sp, CopyFrom, sizeof(SavedImage)); + + /* + * Make our own allocated copies of the heap fields in + * the copied record. This guards against potential + * aliasing problems. + */ + + /* Null out aliased pointers before any allocations + * so that FreeLastSavedImage won't free CopyFrom's + * data if an allocation fails partway through. */ + sp->ImageDesc.ColorMap = NULL; + sp->RasterBits = NULL; + sp->ExtensionBlocks = NULL; + sp->ExtensionBlockCount = 0; + + /* first, the local color map */ + if (CopyFrom->ImageDesc.ColorMap != NULL) { + sp->ImageDesc.ColorMap = GifMakeMapObject( + CopyFrom->ImageDesc.ColorMap->ColorCount, + CopyFrom->ImageDesc.ColorMap->Colors); + if (sp->ImageDesc.ColorMap == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + } + + /* next, the raster */ + sp->RasterBits = (unsigned char *)reallocarray( + NULL, + (CopyFrom->ImageDesc.Height * + CopyFrom->ImageDesc.Width), + sizeof(GifPixelType)); + if (sp->RasterBits == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + memcpy(sp->RasterBits, CopyFrom->RasterBits, + sizeof(GifPixelType) * + CopyFrom->ImageDesc.Height * + CopyFrom->ImageDesc.Width); + + /* finally, the extension blocks */ + if (CopyFrom->ExtensionBlocks != NULL) { + int k; + sp->ExtensionBlocks = + (ExtensionBlock *)calloc( + CopyFrom->ExtensionBlockCount, + sizeof(ExtensionBlock)); + if (sp->ExtensionBlocks == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + for (k = 0; k < CopyFrom->ExtensionBlockCount; + k++) { + ExtensionBlock *dst = + &sp->ExtensionBlocks[k]; + ExtensionBlock *src = + &CopyFrom->ExtensionBlocks[k]; + dst->Function = src->Function; + dst->ByteCount = src->ByteCount; + if (src->ByteCount > 0) { + dst->Bytes = + (GifByteType *)malloc( + src->ByteCount); + if (dst->Bytes == NULL) { + FreeLastSavedImage( + GifFile); + return (SavedImage *)(NULL); + } + memcpy(dst->Bytes, src->Bytes, + src->ByteCount); + } + } + } + } else { + memset((char *)sp, '\0', sizeof(SavedImage)); + } + + return (sp); + } } void GifFreeSavedImages(GifFileType *GifFile) { - SavedImage *sp; + SavedImage *sp; - if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) { - return; - } - for (sp = GifFile->SavedImages; - sp < GifFile->SavedImages + GifFile->ImageCount; sp++) { - if (sp->ImageDesc.ColorMap != NULL) { - GifFreeMapObject(sp->ImageDesc.ColorMap); - sp->ImageDesc.ColorMap = NULL; + if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) { + return; } + for (sp = GifFile->SavedImages; + sp < GifFile->SavedImages + GifFile->ImageCount; sp++) { + if (sp->ImageDesc.ColorMap != NULL) { + GifFreeMapObject(sp->ImageDesc.ColorMap); + sp->ImageDesc.ColorMap = NULL; + } - if (sp->RasterBits != NULL) { - free((char *)sp->RasterBits); + if (sp->RasterBits != NULL) { + free((char *)sp->RasterBits); + } + + GifFreeExtensions(&sp->ExtensionBlockCount, + &sp->ExtensionBlocks); } - - GifFreeExtensions(&sp->ExtensionBlockCount, - &sp->ExtensionBlocks); - } - free((char *)GifFile->SavedImages); - GifFile->SavedImages = NULL; + free((char *)GifFile->SavedImages); + GifFile->SavedImages = NULL; } /* end */ diff --git a/src/java.desktop/share/native/libsplashscreen/giflib/openbsd-reallocarray.c b/src/java.desktop/share/native/libsplashscreen/giflib/openbsd-reallocarray.c index 7420af674c5..57504fceaa9 100644 --- a/src/java.desktop/share/native/libsplashscreen/giflib/openbsd-reallocarray.c +++ b/src/java.desktop/share/native/libsplashscreen/giflib/openbsd-reallocarray.c @@ -22,9 +22,8 @@ * questions. */ -/* $OpenBSD: reallocarray.c,v 1.1 2014/05/08 21:43:49 deraadt Exp $ */ /* - * Copyright (c) 2008 Otto Moerbeek + * SPDX-FileCopyrightText: Copyright (C) 2008 Otto Moerbeek * SPDX-License-Identifier: MIT */ @@ -44,55 +43,55 @@ #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) void *openbsd_reallocarray(void *optr, size_t nmemb, size_t size) { - if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && - nmemb > 0 && SIZE_MAX / nmemb < size) { - errno = ENOMEM; - return NULL; - } - /* - * Head off variations in realloc behavior on different - * platforms (reported by MarkR ) - * - * The behaviour of reallocarray is implementation-defined if - * nmemb or size is zero. It can return NULL or non-NULL - * depending on the platform. - * https://www.securecoding.cert.org/confluence/display/c/MEM04-C.Beware+of+zero-lengthallocations - * - * Here are some extracts from realloc man pages on different platforms. - * - * void realloc( void memblock, size_t size ); - * - * Windows: - * - * If there is not enough available memory to expand the block - * to the given size, the original block is left unchanged, - * and NULL is returned. If size is zero, then the block - * pointed to by memblock is freed; the return value is NULL, - * and memblock is left pointing at a freed block. - * - * OpenBSD: - * - * If size or nmemb is equal to 0, a unique pointer to an - * access protected, zero sized object is returned. Access via - * this pointer will generate a SIGSEGV exception. - * - * Linux: - * - * If size was equal to 0, either NULL or a pointer suitable - * to be passed to free() is returned. - * - * OS X: - * - * If size is zero and ptr is not NULL, a new, minimum sized - * object is allocated and the original object is freed. - * - * It looks like images with zero width or height can trigger - * this, and fuzzing behaviour will differ by platform, so - * fuzzing on one platform may not detect zero-size allocation - * problems on other platforms. - */ - if (size == 0 || nmemb == 0) { - return NULL; - } - return realloc(optr, size * nmemb); + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) { + errno = ENOMEM; + return NULL; + } + /* + * Head off variations in realloc behavior on different + * platforms (reported by MarkR ) + * + * The behaviour of reallocarray is implementation-defined if + * nmemb or size is zero. It can return NULL or non-NULL + * depending on the platform. + * https://www.securecoding.cert.org/confluence/display/c/MEM04-C.Beware+of+zero-lengthallocations + * + * Here are some extracts from realloc man pages on different platforms. + * + * void realloc( void memblock, size_t size ); + * + * Windows: + * + * If there is not enough available memory to expand the block + * to the given size, the original block is left unchanged, + * and NULL is returned. If size is zero, then the block + * pointed to by memblock is freed; the return value is NULL, + * and memblock is left pointing at a freed block. + * + * OpenBSD: + * + * If size or nmemb is equal to 0, a unique pointer to an + * access protected, zero sized object is returned. Access via + * this pointer will generate a SIGSEGV exception. + * + * Linux: + * + * If size was equal to 0, either NULL or a pointer suitable + * to be passed to free() is returned. + * + * OS X: + * + * If size is zero and ptr is not NULL, a new, minimum sized + * object is allocated and the original object is freed. + * + * It looks like images with zero width or height can trigger + * this, and fuzzing behaviour will differ by platform, so + * fuzzing on one platform may not detect zero-size allocation + * problems on other platforms. + */ + if (size == 0 || nmemb == 0) { + return NULL; + } + return realloc(optr, size * nmemb); } diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java b/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java index 78cd4a7e57d..5dcd1b763e1 100644 --- a/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java +++ b/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, 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 @@ -1770,7 +1770,11 @@ public final class XToolkit extends UNIXToolkit implements Runnable { final int altL = keysymToPrimaryKeycode(XKeySymConstants.XK_Alt_L); final int altR = keysymToPrimaryKeycode(XKeySymConstants.XK_Alt_R); final int numLock = keysymToPrimaryKeycode(XKeySymConstants.XK_Num_Lock); - final int modeSwitch = keysymToPrimaryKeycode(XKeySymConstants.XK_Mode_switch); + int modeSwitchTmp = keysymToPrimaryKeycode(XKeySymConstants.XK_Mode_switch); + if (modeSwitchTmp == 0) { + modeSwitchTmp = keysymToPrimaryKeycode(XKeySymConstants.XK_ISO_Level3_Shift); + } + final int modeSwitch = modeSwitchTmp; final int shiftLock = keysymToPrimaryKeycode(XKeySymConstants.XK_Shift_Lock); final int capsLock = keysymToPrimaryKeycode(XKeySymConstants.XK_Caps_Lock); diff --git a/src/java.desktop/unix/native/common/java2d/opengl/GLXSurfaceData.c b/src/java.desktop/unix/native/common/java2d/opengl/GLXSurfaceData.c index 8f264278d9f..0497dbf69fa 100644 --- a/src/java.desktop/unix/native/common/java2d/opengl/GLXSurfaceData.c +++ b/src/java.desktop/unix/native/common/java2d/opengl/GLXSurfaceData.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -40,6 +40,8 @@ #ifndef HEADLESS +#include + extern LockFunc OGLSD_Lock; extern GetRasInfoFunc OGLSD_GetRasInfo; extern UnlockFunc OGLSD_Unlock; @@ -50,6 +52,74 @@ extern void jboolean surfaceCreationFailed = JNI_FALSE; +/** + * Per-Window GLXWindow entry with reference counting. + * Stored in an XContext keyed by the X Window XID. + */ +typedef struct { + GLXWindow glxWindow; + int refCount; +} GLXWindowRef; + +static XContext glxWindowContext; + +/** + * Gets or creates a shared GLXWindow for the given X Window. + * All callers are synchronized by the AWT lock. + */ +static GLXWindow acquireGLXWindow(Window window, GLXFBConfig fbconfig) +{ + if (glxWindowContext == 0) { + glxWindowContext = XUniqueContext(); + } + + XPointer data; + if (XFindContext(awt_display, window, glxWindowContext, &data) == 0) { + GLXWindowRef *ref = (GLXWindowRef *)data; + ref->refCount++; + return ref->glxWindow; + } + + GLXWindow glxWin = j2d_glXCreateWindow(awt_display, fbconfig, window, NULL); + if (glxWin == 0) { + return 0; + } + + GLXWindowRef *ref = malloc(sizeof(*ref)); + if (ref == NULL) { + j2d_glXDestroyWindow(awt_display, glxWin); + return 0; + } + ref->glxWindow = glxWin; + ref->refCount = 1; + if (XSaveContext(awt_display, window, glxWindowContext, (XPointer)ref) != 0) + { + j2d_glXDestroyWindow(awt_display, glxWin); + free(ref); + return 0; + } + return glxWin; +} + +/** + * Decrements the reference count for the GLXWindow associated with the given + * X Window. Destroys it when the count reaches zero. + * All callers are synchronized by the AWT lock. + */ +static void releaseGLXWindow(Window window) +{ + XPointer data; + if (XFindContext(awt_display, window, glxWindowContext, &data) != 0) { + return; + } + GLXWindowRef *ref = (GLXWindowRef *)data; + if (--ref->refCount <= 0) { + j2d_glXDestroyWindow(awt_display, ref->glxWindow); + XDeleteContext(awt_display, window, glxWindowContext); + free(ref); + } +} + #endif /* !HEADLESS */ JNIEXPORT void JNICALL @@ -74,7 +144,7 @@ Java_sun_java2d_opengl_GLXSurfaceData_initOps(JNIEnv *env, jobject glxsd, // later the graphicsConfig will be used for deallocation of oglsdo oglsdo->graphicsConfig = gc; - GLXSDOps *glxsdo = (GLXSDOps *)malloc(sizeof(GLXSDOps)); + GLXSDOps *glxsdo = (GLXSDOps *)calloc(1, sizeof(GLXSDOps)); if (glxsdo == NULL) { JNU_ThrowOutOfMemoryError(env, "creating native GLX ops"); @@ -125,8 +195,13 @@ Java_sun_java2d_opengl_GLXSurfaceData_initOps(JNIEnv *env, jobject glxsd, void OGLSD_DestroyOGLSurface(JNIEnv *env, OGLSDOps *oglsdo) { + GLXSDOps *glxsdo = (GLXSDOps *)oglsdo->privOps; J2dTraceLn(J2D_TRACE_INFO, "OGLSD_DestroyOGLSurface"); - // X Window is free'd later by AWT code... + if (glxsdo != NULL && glxsdo->drawable != 0) { + releaseGLXWindow(glxsdo->window); + glxsdo->drawable = 0; + oglsdo->drawableType = OGLSD_UNDEFINED; + } } /** @@ -296,6 +371,13 @@ OGLSD_InitOGLWindow(JNIEnv *env, OGLSDOps *oglsdo) return JNI_FALSE; } + glxsdo->drawable = acquireGLXWindow(window, + glxsdo->configData->glxInfo->fbconfig); + if (glxsdo->drawable == 0) { + J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_InitOGLWindow: GLXWindow is 0"); + return JNI_FALSE; + } + XGetWindowAttributes(awt_display, window, &attr); oglsdo->width = attr.width; oglsdo->height = attr.height; @@ -304,7 +386,6 @@ OGLSD_InitOGLWindow(JNIEnv *env, OGLSDOps *oglsdo) oglsdo->isOpaque = JNI_TRUE; oglsdo->xOffset = 0; oglsdo->yOffset = 0; - glxsdo->drawable = window; glxsdo->xdrawable = window; J2dTraceLn(J2D_TRACE_VERBOSE, " created window: w=%d h=%d", @@ -333,7 +414,16 @@ OGLSD_SwapBuffers(JNIEnv *env, jlong window) return; } - j2d_glXSwapBuffers(awt_display, (Window)window); + XPointer data; + if (XFindContext(awt_display, (Window)window, glxWindowContext, &data) != 0) + { + J2dRlsTraceLn(J2D_TRACE_ERROR, + "OGLSD_SwapBuffers: GLXWindow not found"); + return; + } + + GLXWindowRef *ref = (GLXWindowRef *)data; + j2d_glXSwapBuffers(awt_display, ref->glxWindow); } // needed by Mac OS X port, no-op on other platforms diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c b/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c index 3b7b2880316..1c29e5168ba 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -400,5 +400,5 @@ Java_com_sun_java_swing_plaf_gtk_GTKLookAndFeel_applyThemeIfNeeded(JNIEnv *env, const gboolean result = gtk->apply_theme_if_needed(); gtk->gdk_threads_leave(); - return result; + return result ? JNI_TRUE : JNI_FALSE; } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java index a7aca0c5ccf..259c32c74f4 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java @@ -51,8 +51,8 @@ import com.sun.java.swing.plaf.windows.TMSchema.State; * Windows rendition of the component. */ public final class WindowsMenuUI extends BasicMenuUI { - private Integer menuBarHeight; - private boolean hotTrackingOn; + protected Integer menuBarHeight; + protected boolean hotTrackingOn; final WindowsMenuItemUIAccessor accessor = new WindowsMenuItemUIAccessor() { diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java index f678be9f1ab..f028e0fbec5 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, 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 @@ -56,7 +56,6 @@ import java.util.LinkedList; import java.util.List; import sun.awt.AWTAccessor; -import sun.awt.AppContext; import sun.awt.DisplayChangedListener; import sun.awt.SunToolkit; import sun.awt.TimedWindowEvent; @@ -84,26 +83,12 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, private TranslucentWindowPainter painter; /* - * A key used for storing a list of active windows in AppContext. The value - * is a list of windows, sorted by the time of activation: later a window is - * activated, greater its index is in the list. - */ - private static final StringBuffer ACTIVE_WINDOWS_KEY = - new StringBuffer("active_windows_list"); - - /* - * Listener for 'activeWindow' KFM property changes. It is added to each - * AppContext KFM. See ActiveWindowListener inner class below. + * Listener for 'activeWindow' KFM property changes. + * See ActiveWindowListener inner class below. */ private static PropertyChangeListener activeWindowListener = new ActiveWindowListener(); - /* - * The object is a listener for the AppContext.GUI_DISPOSED property. - */ - private static final PropertyChangeListener guiDisposedListener = - new GuiDisposedListener(); - /* * Called (on the Toolkit thread) before the appropriate * WindowStateEvent is posted to the EventQueue. @@ -116,18 +101,17 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, private static native void initIDs(); static { initIDs(); + KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + kfm.addPropertyChangeListener("activeWindow", activeWindowListener); } + static List activeWindows = new LinkedList(); // WComponentPeer overrides @Override @SuppressWarnings("unchecked") protected void disposeImpl() { - AppContext appContext = SunToolkit.targetToAppContext(target); - synchronized (appContext) { - List l = (List)appContext.get(ACTIVE_WINDOWS_KEY); - if (l != null) { - l.remove(this); - } + synchronized (activeWindows) { + activeWindows.remove(this); } // Remove ourself from the Map of DisplayChangeListeners @@ -222,8 +206,6 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, Win32GraphicsDevice gd = (Win32GraphicsDevice) gc.getDevice(); gd.addDisplayChangedListener(this); - initActiveWindowsTracking((Window)target); - updateIconImages(); Shape shape = ((Window)target).getShape(); @@ -530,22 +512,15 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, native void modalEnable(Dialog blocker); /* - * Returns all the ever active windows from the current AppContext. + * Returns all the active windows. * The list is sorted by the time of activation, so the latest * active window is always at the end. */ - @SuppressWarnings("unchecked") public static long[] getActiveWindowHandles(Component target) { - AppContext appContext = SunToolkit.targetToAppContext(target); - if (appContext == null) return null; - synchronized (appContext) { - List l = (List)appContext.get(ACTIVE_WINDOWS_KEY); - if (l == null) { - return null; - } - long[] result = new long[l.size()]; - for (int j = 0; j < l.size(); j++) { - result[j] = l.get(j).getHWnd(); + synchronized (activeWindows) { + long[] result = new long[activeWindows.size()]; + for (int j = 0; j < activeWindows.size(); j++) { + result[j] = activeWindows.get(j).getHWnd(); } return result; } @@ -823,58 +798,11 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, } } - /* - * The method maps the list of the active windows to the window's AppContext, - * then the method registers ActiveWindowListener, GuiDisposedListener listeners; - * it executes the initilialization only once per AppContext. - */ - @SuppressWarnings("unchecked") - private static void initActiveWindowsTracking(Window w) { - AppContext appContext = AppContext.getAppContext(); - synchronized (appContext) { - List l = (List)appContext.get(ACTIVE_WINDOWS_KEY); - if (l == null) { - l = new LinkedList(); - appContext.put(ACTIVE_WINDOWS_KEY, l); - appContext.addPropertyChangeListener(AppContext.GUI_DISPOSED, guiDisposedListener); - - KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); - kfm.addPropertyChangeListener("activeWindow", activeWindowListener); - } - } - } - - /* - * The GuiDisposedListener class listens for the AppContext.GUI_DISPOSED property, - * it removes the list of the active windows from the disposed AppContext and - * unregisters ActiveWindowListener listener. - */ - private static final class GuiDisposedListener implements PropertyChangeListener { - @Override - public void propertyChange(PropertyChangeEvent e) { - boolean isDisposed = (Boolean)e.getNewValue(); - if (isDisposed != true) { - if (log.isLoggable(PlatformLogger.Level.FINE)) { - log.fine(" Assertion (newValue != true) failed for AppContext.GUI_DISPOSED "); - } - } - AppContext appContext = AppContext.getAppContext(); - synchronized (appContext) { - appContext.remove(ACTIVE_WINDOWS_KEY); - appContext.removePropertyChangeListener(AppContext.GUI_DISPOSED, this); - - KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); - kfm.removePropertyChangeListener("activeWindow", activeWindowListener); - } - } - } - /* * Static inner class, listens for 'activeWindow' KFM property changes and - * updates the list of active windows per AppContext, so the latest active - * window is always at the end of the list. The list is stored in AppContext. + * updates the list of active windows so the latest active + * window is always at the end of the list. */ - @SuppressWarnings("unchecked") private static final class ActiveWindowListener implements PropertyChangeListener { @Override public void propertyChange(PropertyChangeEvent e) { @@ -882,15 +810,11 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, if (w == null) { return; } - AppContext appContext = SunToolkit.targetToAppContext(w); - synchronized (appContext) { + synchronized (activeWindows) { WWindowPeer wp = AWTAccessor.getComponentAccessor().getPeer(w); // add/move wp to the end of the list - List l = (List)appContext.get(ACTIVE_WINDOWS_KEY); - if (l != null) { - l.remove(wp); - l.add(wp); - } + activeWindows.remove(wp); + activeWindows.add(wp); } } } diff --git a/src/java.instrument/share/native/libinstrument/JavaExceptions.c b/src/java.instrument/share/native/libinstrument/JavaExceptions.c index 0a787ce4150..45c31e329d0 100644 --- a/src/java.instrument/share/native/libinstrument/JavaExceptions.c +++ b/src/java.instrument/share/native/libinstrument/JavaExceptions.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -68,9 +68,20 @@ forceFallback(jthrowable potentialException) { jboolean initializeFallbackError(JNIEnv* jnienv) { jplis_assert(isSafeForJNICalls(jnienv)); - sFallbackInternalError = createInternalError(jnienv, NULL); + jthrowable localRef = createInternalError(jnienv, NULL); + if (localRef == NULL) { + return JNI_FALSE; + } + + jthrowable globalRef = (*jnienv)->NewGlobalRef(jnienv, localRef); + if (globalRef == NULL) { + return JNI_FALSE; + } + + sFallbackInternalError = globalRef; jplis_assert(isSafeForJNICalls(jnienv)); - return (sFallbackInternalError != NULL); + + return JNI_TRUE; } diff --git a/src/java.logging/share/classes/java/util/logging/LogManager.java b/src/java.logging/share/classes/java/util/logging/LogManager.java index 9c9c708a062..102f4bac6e4 100644 --- a/src/java.logging/share/classes/java/util/logging/LogManager.java +++ b/src/java.logging/share/classes/java/util/logging/LogManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -736,41 +736,56 @@ public class LogManager { logger.setLevel(level); } - // instantiation of the handler is done in the LogManager.addLogger - // implementation as a handler class may be only visible to LogManager - // subclass for the custom log manager case - processParentHandlers(logger, name, VisitedLoggers.NEVER); + // We need to make sure that loggers created by processParentHandlers + // will not be garbage collected before the child/parent + // pointers are updated. We use an ArrayList to temporarily + // store these loggers, until the parent/child relationship + // have been updated + final List saved = new ArrayList<>(); + try { - // Find the new node and its parent. - LogNode node = getNode(name); - node.loggerRef = ref; - Logger parent = null; - LogNode nodep = node.parent; - while (nodep != null) { - LoggerWeakRef nodeRef = nodep.loggerRef; - if (nodeRef != null) { - parent = nodeRef.get(); - if (parent != null) { - break; + // always return false, to make sure we process all loggers from + // root to child. + Predicate visited = (l) -> saved.add(l) && false; + + // instantiation of the handler is done in the LogManager.addLogger + // implementation as a handler class may be only visible to LogManager + // subclass for the custom log manager case + processParentHandlers(logger, name, visited); + + // Find the new node and its parent. + LogNode node = getNode(name); + node.loggerRef = ref; + Logger parent = null; + LogNode nodep = node.parent; + while (nodep != null) { + LoggerWeakRef nodeRef = nodep.loggerRef; + if (nodeRef != null) { + parent = nodeRef.get(); + if (parent != null) { + break; + } } + nodep = nodep.parent; } - nodep = nodep.parent; - } - if (parent != null) { - logger.setParent(parent); - } - // Walk over the children and tell them we are their new parent. - node.walkAndSetParent(logger); - // new LogNode is ready so tell the LoggerWeakRef about it - ref.setNode(node); + if (parent != null) { + logger.setParent(parent); + } + // Walk over the children and tell them we are their new parent. + node.walkAndSetParent(logger); + // new LogNode is ready so tell the LoggerWeakRef about it + ref.setNode(node); - // Do not publish 'ref' in namedLoggers before the logger tree - // is fully updated - because the named logger will be visible as - // soon as it is published in namedLoggers (findLogger takes - // benefit of the ConcurrentHashMap implementation of namedLoggers - // to avoid synchronizing on retrieval when that is possible). - namedLoggers.put(name, ref); + // Do not publish 'ref' in namedLoggers before the logger tree + // is fully updated - because the named logger will be visible as + // soon as it is published in namedLoggers (findLogger takes + // benefit of the ConcurrentHashMap implementation of namedLoggers + // to avoid synchronizing on retrieval when that is possible). + namedLoggers.put(name, ref); + } finally { + saved.clear(); + } return true; } @@ -1647,11 +1662,6 @@ public class LogManager { public void clear() { if (visited != null) visited.clear(); } - - // An object that considers that no logger has ever been visited. - // This is used when processParentHandlers is called from - // LoggerContext.addLocalLogger - static final VisitedLoggers NEVER = new VisitedLoggers(null); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java index 9ffab9fd961..f1c6676d087 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -993,10 +993,12 @@ public class Types { @Override public boolean test(Symbol sym) { + List msyms; return sym.kind == MTH && (sym.flags() & (ABSTRACT | DEFAULT)) == ABSTRACT && !overridesObjectMethod(origin, sym) && - (interfaceCandidates(origin.type, (MethodSymbol)sym).head.flags() & DEFAULT) == 0; + (msyms = interfaceCandidates(origin.type, (MethodSymbol)sym)).nonEmpty() && + (msyms.head.flags() & DEFAULT) == 0; } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java index f865afe11fb..118d761573b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java @@ -861,7 +861,6 @@ public class Annotate { if (!chk.validateAnnotationDeferErrors(annoTree)) log.error(annoTree.pos(), Errors.DuplicateAnnotationInvalidRepeated(origAnnoType)); - c = attributeAnnotation(annoTree, targetContainerType, ctx.env); c.setSynthesized(true); @SuppressWarnings("unchecked") diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index b7bf48b4a12..017d740dc0a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -69,6 +69,7 @@ import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.ByteBuffer.UnderflowException; import com.sun.tools.javac.util.DefinedBy.Api; import com.sun.tools.javac.util.JCDiagnostic.Fragment; +import com.sun.tools.javac.util.Log.DeferredDiagnosticHandler; import static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Kinds.Kind.*; @@ -2043,15 +2044,27 @@ public class ClassReader { } Attribute.Compound deproxyCompound(CompoundAnnotationProxy a) { - Type annotationType = resolvePossibleProxyType(a.type); - ListBuffer> buf = new ListBuffer<>(); - for (List> l = a.values; - l.nonEmpty(); - l = l.tail) { - MethodSymbol meth = findAccessMethod(annotationType, l.head.fst); - buf.append(new Pair<>(meth, deproxy(meth.type.getReturnType(), l.head.snd))); + DeferredDiagnosticHandler deferred = log.new DeferredDiagnosticHandler(); + Type annotationType = syms.objectType; + try { + annotationType = resolvePossibleProxyType(a.type); + ListBuffer> buf = new ListBuffer<>(); + for (List> l = a.values; + l.nonEmpty(); + l = l.tail) { + MethodSymbol meth = findAccessMethod(annotationType, l.head.fst); + buf.append(new Pair<>(meth, deproxy(meth.type.getReturnType(), l.head.snd))); + } + return new Attribute.Compound(annotationType, buf.toList()); + } finally { + if (!annotationType.tsym.type.hasTag(TypeTag.ERROR)) { + //if the annotation type does not exists + //throw away warnings reported while de-proxying the annotation, + //as the annotation's library is probably missing from the classpath: + deferred.reportDeferredDiagnostics(); + } + log.popDiagnosticHandler(deferred); } - return new Attribute.Compound(annotationType, buf.toList()); } MethodSymbol findAccessMethod(Type container, Name name) { @@ -2146,15 +2159,21 @@ public class ClassReader { failure = ex; } if (enumerator == null) { - if (failure != null) { - log.warning(Warnings.UnknownEnumConstantReason(currentClassFile, - enumTypeSym, - proxy.enumerator, - failure.getDiagnostic())); - } else { - log.warning(Warnings.UnknownEnumConstant(currentClassFile, - enumTypeSym, - proxy.enumerator)); + // The enumerator wasn't found: emit a warning and recover + JavaFileObject prevSource = log.useSource(requestingOwner.classfile); + try { + if (failure != null) { + log.warning(LintWarnings.UnknownEnumConstantReason(currentClassFile, + enumTypeSym, + proxy.enumerator, + failure.getDiagnostic())); + } else { + log.warning(LintWarnings.UnknownEnumConstant(currentClassFile, + enumTypeSym, + proxy.enumerator)); + } + } finally { + log.useSource(prevSource); } result = new Attribute.Enum(enumTypeSym.type, new VarSymbol(0, proxy.enumerator, syms.botType, enumTypeSym)); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java index 94292d9a348..269d2f5de62 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -823,9 +823,9 @@ public class JavaCompiler { c, () -> diagFactory.fragment(Fragments.UserSelectedCompletionFailure), dcfh); } JavaFileObject filename = c.classfile; - JavaFileObject prev = log.useSource(filename); if (tree == null) { + JavaFileObject prev = log.useSource(filename); try { tree = parse(filename, filename.getCharContent(false)); } catch (IOException e) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index 915d7f8a8d8..58a5333ce4c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -2521,10 +2521,12 @@ compiler.err.cant.attach.type.annotations=\ {3} # 0: file object, 1: symbol, 2: name +# lint: classfile compiler.warn.unknown.enum.constant=\ unknown enum constant {1}.{2} # 0: file object, 1: symbol, 2: name, 3: message segment +# lint: classfile compiler.warn.unknown.enum.constant.reason=\ unknown enum constant {1}.{2}\n\ reason: {3} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadLocalAllocBuffer.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadLocalAllocBuffer.java index 11f03a6003e..683e4b67935 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadLocalAllocBuffer.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadLocalAllocBuffer.java @@ -76,10 +76,9 @@ public class ThreadLocalAllocBuffer extends VMObject { private long endReserve() { long labAlignmentReserve = VM.getVM().getLabAlignmentReserve(); - long reserveForAllocationPrefetch = VM.getVM().getReserveForAllocationPrefetch(); long heapWordSize = VM.getVM().getHeapWordSize(); - return Math.max(labAlignmentReserve, reserveForAllocationPrefetch) * heapWordSize; + return labAlignmentReserve * heapWordSize; } /** Support for iteration over heap -- not sure how this will diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java index dc27a4fc59e..1607563150a 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java @@ -123,7 +123,6 @@ public class VM { private int invocationEntryBCI; private ReversePtrs revPtrs; private VMRegImpl vmregImpl; - private int reserveForAllocationPrefetch; private int labAlignmentReserve; // System.getProperties from debuggee VM @@ -447,8 +446,6 @@ public class VM { boolType = (CIntegerType) db.lookupType("bool"); Type threadLocalAllocBuffer = db.lookupType("ThreadLocalAllocBuffer"); - CIntegerField reserveForAllocationPrefetchField = threadLocalAllocBuffer.getCIntegerField("_reserve_for_allocation_prefetch"); - reserveForAllocationPrefetch = (int)reserveForAllocationPrefetchField.getCInteger(intType); Type collectedHeap = db.lookupType("CollectedHeap"); CIntegerField labAlignmentReserveField = collectedHeap.getCIntegerField("_lab_alignment_reserve"); @@ -915,10 +912,6 @@ public class VM { return vmInternalInfo; } - public int getReserveForAllocationPrefetch() { - return reserveForAllocationPrefetch; - } - public int getLabAlignmentReserve() { return labAlignmentReserve; } diff --git a/src/jdk.jartool/share/man/jar.md b/src/jdk.jartool/share/man/jar.md index d944afcfb7f..658fa0cb4fa 100644 --- a/src/jdk.jartool/share/man/jar.md +++ b/src/jdk.jartool/share/man/jar.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1997, 2026, 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 @@ -84,29 +84,29 @@ appropriate operation arguments described in this section. You can mix an operation argument with other one-letter options. Generally the operation argument is the first argument specified on the command line. -`-c` or `--create` +[`-c`]{#option--create} or `--create` : Creates the archive. -`-i` *FILE* or `--generate-index=`*FILE* +[`-i`]{#option--generate-index} *FILE* or `--generate-index=`*FILE* : Generates index information for the specified JAR file. This option is deprecated and may be removed in a future release. -`-t` or `--list` +[`-t`]{#option--list} or `--list` : Lists the table of contents for the archive. -`-u` or `--update` +[`-u`]{#option--update} or `--update` : Updates an existing JAR file. -`-x` or `--extract` +[`-x`]{#option--extract} or `--extract` : Extracts the named (or all) files from the archive. If a file with the same name appears more than once in the archive, each copy will be extracted, with later copies overwriting (replacing) earlier copies unless -k is specified. -`-d` or `--describe-module` +[`-d`]{#option--describe-module} or `--describe-module` : Prints the module descriptor or automatic module name. -`--validate` +[`--validate`]{#option--validate} : Validate the contents of the JAR file. See `Integrity of a JAR File` section below for more details. @@ -115,7 +115,7 @@ argument is the first argument specified on the command line. You can use the following options to customize the actions of any operation mode included in the `jar` command. -`-C` *DIR* +[`-C`]{#option-C} *DIR* : When used with the create operation mode, changes the specified directory and includes the *files* specified at the end of the command line. @@ -126,10 +126,10 @@ mode included in the `jar` command. where the JAR file will be extracted. Unlike with the create operation mode, this option can be specified only once with the extract operation mode. -`-f` *FILE* or `--file=`*FILE* +[`-f`]{#option--file} *FILE* or `--file=`*FILE* : Specifies the archive file name. -`--release` *VERSION* +[`--release`]{#option--release} *VERSION* : Creates a multirelease JAR file. Places all files specified after the option into a versioned directory of the JAR file named `META-INF/versions/`*VERSION*`/`, where *VERSION* must be must be a @@ -149,26 +149,26 @@ mode included in the `jar` command. You can use the following options to customize the actions of the create and the update main operation modes: -`-e` *CLASSNAME* or `--main-class=`*CLASSNAME* +[`-e`]{#option--main-class} *CLASSNAME* or `--main-class=`*CLASSNAME* : Specifies the application entry point for standalone applications bundled into a modular or executable modular JAR file. -`-m` *FILE* or `--manifest=`*FILE* +[`-m`]{#option--manifest} *FILE* or `--manifest=`*FILE* : Includes the manifest information from the given manifest file. -`-M` or `--no-manifest` +[`-M`]{#option--no-manifest} or `--no-manifest` : Doesn't create a manifest file for the entries. -`--module-version=`*VERSION* +[`--module-version=`]{#option--module-version}*VERSION* : Specifies the module version, when creating or updating a modular JAR file, or updating a non-modular JAR file. -`--hash-modules=`*PATTERN* +[`--hash-modules=`]{#option--hash-modules}*PATTERN* : Computes and records the hashes of modules matched by the given pattern and that depend upon directly or indirectly on a modular JAR file being created or a non-modular JAR file being updated. -`-p` or `--module-path` +[`-p`]{#option--module-path} or `--module-path` : Specifies the location of module dependence for generating the hash. `@`*file* @@ -181,20 +181,20 @@ You can use the following options to customize the actions of the create (`-c` or `--create`) the update (`-u` or `--update` ) and the generate-index (`-i` or `--generate-index=`*FILE*) main operation modes: -`-0` or `--no-compress` +[`-0`]{#option--no-compress} or `--no-compress` : Stores without using ZIP compression. -`--date=`*TIMESTAMP* +[`--date=`]{#option--date}*TIMESTAMP* : The timestamp in ISO-8601 extended offset date-time with optional time-zone format, to use for the timestamp of the entries, e.g. "2022-02-12T12:30:00-05:00". ## Operation Modifiers Valid Only in Extract Mode -`--dir` *DIR* +[`--dir`]{#option--dir} *DIR* : Directory into which the JAR file will be extracted. -`-k` or `--keep-old-files` +[`-k`]{#option--keep-old-files} or `--keep-old-files` : Do not overwrite existing files. If a Jar file entry with the same name exists in the target directory, the existing file will not be overwritten. diff --git a/src/jdk.jdi/share/classes/com/sun/tools/example/debug/expr/LValue.java b/src/jdk.jdi/share/classes/com/sun/tools/example/debug/expr/LValue.java index 3855d1bd51c..43b2c420147 100644 --- a/src/jdk.jdi/share/classes/com/sun/tools/example/debug/expr/LValue.java +++ b/src/jdk.jdi/share/classes/com/sun/tools/example/debug/expr/LValue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -73,11 +73,11 @@ abstract class LValue { setValue0(value); } catch (InvalidTypeException exc) { throw new ParseException( - "Attempt to set value of incorrect type" + + "Attempt to set value of incorrect type: " + exc); } catch (ClassNotLoadedException exc) { throw new ParseException( - "Attempt to set value before " + exc.className() + " was loaded" + + "Attempt to set value before " + exc.className() + " was loaded: " + exc); } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Field.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Field.java index b7fc4381670..f9c2ab351bf 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Field.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Field.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -118,6 +118,9 @@ final class Field { // An integral type (byte, short, int, long) boolean integralType; + // An integral type that should be treated like a symbol, e.g. PID. + boolean identifier; + // A java.time.Duration boolean timespan; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldBuilder.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldBuilder.java index 64791b1976a..8d0b3371c7d 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldBuilder.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -274,6 +274,7 @@ final class FieldBuilder { case "int", "long", "short", "byte": field.integralType = true; field.alignLeft = false; + field.identifier = fieldName.equals("id") || fieldName.endsWith("Id") || field.label.endsWith("Identifier"); break; case "float", "double": field.fractionalType = true; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldFormatter.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldFormatter.java index 989e2231eb1..ec032ce9f34 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldFormatter.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -165,7 +165,7 @@ public class FieldFormatter { return object + " Hz"; } } - if (object instanceof Number number) { + if (object instanceof Number number && !field.identifier) { return ValueFormatter.formatNumber(number); } return object.toString(); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/TimedMethod.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/TimedMethod.java index bd4f2262444..2651f5b473a 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/TimedMethod.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/TimedMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -46,10 +46,10 @@ record TimedMethod(AtomicLong invocations, AtomicLong time, AtomicLong minimum, while (true) { long max = maximum.get(); if (duration <= max) { - return; + break; } if (maximum.weakCompareAndSetVolatile(max, duration)) { - return; + break; } } } diff --git a/src/jdk.jlink/share/man/jlink.md b/src/jdk.jlink/share/man/jlink.md index 5c77202434c..b95424fdde9 100644 --- a/src/jdk.jlink/share/man/jlink.md +++ b/src/jdk.jlink/share/man/jlink.md @@ -57,14 +57,14 @@ Developers are responsible for updating their custom runtime images. ## jlink Options -`--add-modules` *mod*\[`,`*mod*...\] +[`--add-modules`]{#option--add-modules} *mod*\[`,`*mod*...\] : Adds the named modules, *mod*, to the default set of root modules. The default set of root modules is empty. -`--bind-services` +[`--bind-services`]{#option--bind-services} : Link service provider modules and their dependencies. -`-c zip-{0-9}` or `--compress=zip-{0-9}` +[`-c zip-{0-9}`]{#option--compress} or `--compress=zip-{0-9}` : Enable compression of resources. The accepted values are: zip-{0-9}, where zip-0 provides no compression, and zip-9 provides the best compression. Default is zip-6. @@ -75,37 +75,37 @@ Developers are responsible for updating their custom runtime images. - `1`: Constant string sharing - `2`: ZIP. Use zip-6 instead. -`--disable-plugin` *pluginname* +[`--disable-plugin`]{#option--disable-plugin} *pluginname* : Disables the specified plug-in. See [jlink Plug-ins] for the list of supported plug-ins. -`--endian` {`little`\|`big`} +[`--endian`]{#option--endian} {`little`\|`big`} : Specifies the byte order of the generated image. The default value is the format of your system's architecture. `-h` or `--help` : Prints the help message. -`--ignore-signing-information` +[`--ignore-signing-information`]{#option--ignore-signing-information} : Suppresses a fatal error when signed modular JARs are linked in the runtime image. The signature-related files of the signed modular JARs aren't copied to the runtime image. -`--launcher` *command*`=`*module* or `--launcher` *command*`=`*module*`/`*main* +[`--launcher`]{#option--launcher} *command*`=`*module* or `--launcher` *command*`=`*module*`/`*main* : Specifies the launcher command name for the module or the command name for the module and main class (the module and the main class names are separated by a slash (`/`)). -`--limit-modules` *mod*\[`,`*mod*...\] +[`--limit-modules`]{#option--limit-modules} *mod*\[`,`*mod*...\] : Limits the universe of observable modules to those in the transitive closure of the named modules, `mod`, plus the main module, if any, plus any further modules specified in the `--add-modules` option. -`--list-plugins` +[`--list-plugins`]{#option--list-plugins} : Lists available plug-ins, which you can access through command-line options; see [jlink Plug-ins]. -`-p` or `--module-path` *modulepath* +[`-p`]{#option-module-path} or `--module-path` *modulepath* : Specifies the module path. If this option is not specified, then the default module path is @@ -114,19 +114,19 @@ Developers are responsible for updating their custom runtime images. `java.base` module cannot be resolved from it, then the `jlink` command appends `$JAVA_HOME/jmods` to the module path. -`--no-header-files` +[`--no-header-files`]{#option--no-header-files} : Excludes header files. -`--no-man-pages` +[`--no-man-pages`]{#option--no-man-pages} : Excludes man pages. -`--output` *path* +[`--output`]{#option--output} *path* : Specifies the location of the generated runtime image. -`--save-opts` *filename* +[`--save-opts`]{#option--save-opts} *filename* : Saves `jlink` options in the specified file. -`--suggest-providers` \[*name*`,` ...\] +[`--suggest-providers`]{#option--suggest-providers} \[*name*`,` ...\] : Suggest providers that implement the given service types from the module path. diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java index 0791c79c662..1f954036431 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java @@ -27,6 +27,7 @@ package jdk.jpackage.internal; import static jdk.jpackage.internal.FromOptions.buildApplicationBuilder; import static jdk.jpackage.internal.FromOptions.createPackageBuilder; import static jdk.jpackage.internal.LinuxPackagingPipeline.APPLICATION_LAYOUT; +import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_LINUX_RPM; import static jdk.jpackage.internal.cli.StandardOption.LINUX_APP_CATEGORY; import static jdk.jpackage.internal.cli.StandardOption.LINUX_DEB_MAINTAINER_EMAIL; import static jdk.jpackage.internal.cli.StandardOption.LINUX_MENU_GROUP; @@ -39,6 +40,7 @@ import static jdk.jpackage.internal.model.StandardPackageType.LINUX_DEB; import static jdk.jpackage.internal.model.StandardPackageType.LINUX_RPM; import jdk.jpackage.internal.cli.Options; +import jdk.jpackage.internal.model.DottedVersion; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.LinuxApplication; import jdk.jpackage.internal.model.LinuxDebPackage; @@ -67,6 +69,10 @@ final class LinuxFromOptions { appBuilder.launchers().map(LinuxPackagingPipeline::normalizeShortcuts).ifPresent(appBuilder::launchers); + if (OptionUtils.bundlingOperation(options) == CREATE_LINUX_RPM) { + appBuilder.derivedVersionNormalizer(LinuxFromOptions::normalizeRpmVersion); + } + return LinuxApplication.create(appBuilder.create()); } @@ -118,4 +124,15 @@ final class LinuxFromOptions { return pkgBuilder; } + private static String normalizeRpmVersion(String version) { + // RPM does not support "-" symbol in version. In some case + // we might have "-" from "release" file version. + // Normalize version if it has "-" symbols. All other supported version + // formats by "release" file should be supported by RPM. + if (version.contains("-")) { + return DottedVersion.lazy(version).toComponentsString(); + } + + return version; + } } diff --git a/src/jdk.jpackage/linux/native/applauncher/LinuxLauncher.c b/src/jdk.jpackage/linux/native/applauncher/LinuxLauncher.c index 7fb8d9f53e9..7db42bc2dbd 100644 --- a/src/jdk.jpackage/linux/native/applauncher/LinuxLauncher.c +++ b/src/jdk.jpackage/linux/native/applauncher/LinuxLauncher.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -133,6 +133,43 @@ cleanup: } +static ssize_t readFully(const int fd, void* buf, const size_t len) { + size_t nRead = 0; + ssize_t n; + + while (nRead < len) { + n = read(fd, (char*)buf + nRead, len - nRead); + if (n == -1) { + if (errno == EINTR) { + continue; + } + return -1; + } + if (n == 0) { + break; + } + nRead += (size_t)n; + } + return (ssize_t)nRead; +} + +static ssize_t writeFully(const int fd, const void* buf, const size_t len) { + size_t nWritten = 0; + ssize_t n; + + while (nWritten < len) { + n = write(fd, (const char*)buf + nWritten, len - nWritten); + if (n == -1) { + if (errno == EINTR) { + continue; + } + return -1; + } + nWritten += (size_t)n; + } + return (ssize_t)nWritten; +} + static void closePipeEnd(int* pipefd, int idx) { if (pipefd[idx] >= 0) { close(pipefd[idx]); @@ -190,21 +227,24 @@ int main(int argc, char *argv[]) { jvmLauncherData = initJvmlLauncherData(&jvmLauncherDataBufferSize); if (jvmLauncherData) { /* Buffer size */ - if (write(pipefd[1], &jvmLauncherDataBufferSize, - sizeof(jvmLauncherDataBufferSize)) == -1) { + if (writeFully(pipefd[1], &jvmLauncherDataBufferSize, + sizeof(jvmLauncherDataBufferSize)) != + sizeof(jvmLauncherDataBufferSize)) { JP_LOG_ERRNO; goto cleanup; } if (jvmLauncherDataBufferSize) { /* Buffer address */ - if (write(pipefd[1], &jvmLauncherData, - sizeof(jvmLauncherData)) == -1) { + if (writeFully(pipefd[1], &jvmLauncherData, + sizeof(jvmLauncherData)) != + sizeof(jvmLauncherData)) { JP_LOG_ERRNO; goto cleanup; } /* Buffer data */ - if (write(pipefd[1], jvmLauncherData, - jvmLauncherDataBufferSize) == -1) { + if (writeFully(pipefd[1], jvmLauncherData, + jvmLauncherDataBufferSize) != + jvmLauncherDataBufferSize) { JP_LOG_ERRNO; goto cleanup; } @@ -218,8 +258,9 @@ int main(int argc, char *argv[]) { /* Close unused write end */ closePipeEnd(pipefd, 1); - if (read(pipefd[0], &jvmLauncherDataBufferSize, - sizeof(jvmLauncherDataBufferSize)) == -1) { + if (readFully(pipefd[0], &jvmLauncherDataBufferSize, + sizeof(jvmLauncherDataBufferSize)) != + sizeof(jvmLauncherDataBufferSize)) { JP_LOG_ERRNO; goto cleanup; } @@ -229,7 +270,8 @@ int main(int argc, char *argv[]) { goto cleanup; } - if (read(pipefd[0], &baseAddress, sizeof(baseAddress)) == -1) { + if (readFully(pipefd[0], &baseAddress, sizeof(baseAddress)) != + sizeof(baseAddress)) { JP_LOG_ERRNO; goto cleanup; } @@ -240,8 +282,8 @@ int main(int argc, char *argv[]) { goto cleanup; } - if (read(pipefd[0], jvmLauncherData, - jvmLauncherDataBufferSize) == -1) { + if (readFully(pipefd[0], jvmLauncherData, jvmLauncherDataBufferSize) != + jvmLauncherDataBufferSize) { JP_LOG_ERRNO; goto cleanup; } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/ActiveKeychainList.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/ActiveKeychainList.java new file mode 100644 index 00000000000..ab41fc0a60e --- /dev/null +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/ActiveKeychainList.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2025, 2026, 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. + */ + +package jdk.jpackage.internal; + +import java.io.Closeable; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import jdk.internal.util.OSVersion; + +final class ActiveKeychainList implements Closeable { + + static Optional createForPlatform(List keychains) throws IOException { + if (!keychains.isEmpty() && Globals.instance().findBooleanProperty(ActiveKeychainList.class).orElseGet(ActiveKeychainList::isRequired)) { + return Optional.of(new ActiveKeychainList(keychains)); + } else { + return Optional.empty(); + } + } + + static Optional createForPlatform(Keychain... keychains) throws IOException { + return createForPlatform(List.of(keychains)); + } + + @SuppressWarnings("try") + static void withKeychains(Consumer> keychainConsumer, List keychains) throws IOException { + var keychainList = createForPlatform(keychains); + if (keychainList.isEmpty()) { + keychainConsumer.accept(keychains); + } else { + try (var kl = keychainList.get()) { + keychainConsumer.accept(keychains); + } + } + } + + static void withKeychain(Consumer keychainConsumer, Keychain keychain) throws IOException { + + Objects.requireNonNull(keychainConsumer); + withKeychains(keychains -> { + keychainConsumer.accept(keychains.getFirst()); + }, List.of(keychain)); + } + + ActiveKeychainList(List requestedKeychains, List currentKeychains, boolean force) throws IOException { + this.requestedKeychains = List.copyOf(requestedKeychains); + this.oldKeychains = List.copyOf(currentKeychains); + + final List cmdline = new ArrayList<>(LIST_KEYCHAINS_CMD_PREFIX); + addKeychains(cmdline, oldKeychains); + + if (force) { + this.currentKeychains = requestedKeychains; + restoreKeychainsCmd = List.copyOf(cmdline); + cmdline.subList(LIST_KEYCHAINS_CMD_PREFIX.size(), cmdline.size()).clear(); + addKeychains(cmdline, requestedKeychains); + } else { + final var currentKeychainPaths = oldKeychains.stream().map(Keychain::path).toList(); + + final var missingKeychains = requestedKeychains.stream().filter(k -> { + return !currentKeychainPaths.contains(k.path()); + }).toList(); + + if (missingKeychains.isEmpty()) { + this.currentKeychains = oldKeychains; + restoreKeychainsCmd = List.of(); + } else { + this.currentKeychains = Stream.of(oldKeychains, missingKeychains) + .flatMap(List::stream).collect(Collectors.toUnmodifiableList()); + restoreKeychainsCmd = List.copyOf(cmdline); + addKeychains(cmdline, missingKeychains); + } + } + + Executor.of(cmdline).executeExpectSuccess(); + } + + ActiveKeychainList(List keychains) throws IOException { + this(keychains, Keychain.listKeychains(), false); + } + + List requestedKeychains() { + return requestedKeychains; + } + + List currentKeychains() { + return currentKeychains; + } + + List restoreKeychains() { + return oldKeychains; + } + + @Override + public void close() throws IOException { + if (!restoreKeychainsCmd.isEmpty()) { + Executor.of(restoreKeychainsCmd).executeExpectSuccess(); + } + } + + private static void addKeychains(List cmdline, List keychains) { + cmdline.addAll(keychains.stream().map(Keychain::asCliArg).toList()); + } + + private static boolean isRequired() { + // Required for OS X 10.12+ + return 0 <= OSVersion.current().compareTo(new OSVersion(10, 12)); + } + + private final List requestedKeychains; + private final List currentKeychains; + private final List oldKeychains; + private final List restoreKeychainsCmd; + + private final static List LIST_KEYCHAINS_CMD_PREFIX = List.of("/usr/bin/security", "list-keychains", "-s"); +} diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageInfoPListFile.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageInfoPListFile.java deleted file mode 100644 index 602e147a970..00000000000 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageInfoPListFile.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2025, 2026, 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. - */ -package jdk.jpackage.internal; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import jdk.jpackage.internal.model.DottedVersion; -import jdk.jpackage.internal.util.PListReader; -import org.xml.sax.SAXException; - -/** - * Mandatory elements of Info.plist file of app image. - */ -record AppImageInfoPListFile(String bundleIdentifier, String bundleName, String copyright, - DottedVersion shortVersion, DottedVersion bundleVersion, String category) { - - static final class InvalidPlistFileException extends Exception { - InvalidPlistFileException(Throwable cause) { - super(cause); - } - - private static final long serialVersionUID = 1L; - } - - static AppImageInfoPListFile loadFromInfoPList(Path infoPListFile) - throws IOException, InvalidPlistFileException, SAXException { - - final var plistReader = new PListReader(Files.readAllBytes(infoPListFile)); - - try { - return new AppImageInfoPListFile( - plistReader.queryValue("CFBundleIdentifier"), - plistReader.queryValue("CFBundleName"), - plistReader.queryValue("NSHumanReadableCopyright"), - DottedVersion.greedy(plistReader.queryValue("CFBundleShortVersionString")), - DottedVersion.greedy(plistReader.queryValue("CFBundleVersion")), - plistReader.queryValue("LSApplicationCategoryType")); - } catch (Exception ex) { - throw new InvalidPlistFileException(ex); - } - } -} diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java index 43590fb5e2c..cdccd488ed6 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java @@ -36,20 +36,24 @@ import java.util.stream.Stream; import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.AppImageSigningConfig; import jdk.jpackage.internal.model.Application; +import jdk.jpackage.internal.model.ApplicationLaunchers; +import jdk.jpackage.internal.model.ExternalApplication; +import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.MacApplication; import jdk.jpackage.internal.model.MacApplicationMixin; -import jdk.jpackage.internal.model.JPackageException; +import jdk.jpackage.internal.util.PListReader; +import jdk.jpackage.internal.util.Result; import jdk.jpackage.internal.util.RootedPath; final class MacApplicationBuilder { - MacApplicationBuilder(Application app) { - this.app = Objects.requireNonNull(app); + MacApplicationBuilder(ApplicationBuilder appBuilder) { + this.superBuilder = Objects.requireNonNull(appBuilder); } private MacApplicationBuilder(MacApplicationBuilder other) { - this(other.app); + this(other.superBuilder.copy()); icon = other.icon; bundleName = other.bundleName; bundleIdentifier = other.bundleIdentifier; @@ -94,18 +98,28 @@ final class MacApplicationBuilder { return this; } + Optional externalApplication() { + return superBuilder.externalApplication(); + } + + Optional launchers() { + return superBuilder.launchers(); + } + MacApplication create() { if (externalInfoPlistFile != null) { return createCopyForExternalInfoPlistFile().create(); } + var app = superBuilder.create(); + validateAppVersion(app); validateAppContentDirs(app); final var mixin = new MacApplicationMixin.Stub( validatedIcon(), - validatedBundleName(), - validatedBundleIdentifier(), + validatedBundleName(app), + validatedBundleIdentifier(app), validatedCategory(), appStore, createSigningConfig()); @@ -161,39 +175,58 @@ final class MacApplicationBuilder { } private MacApplicationBuilder createCopyForExternalInfoPlistFile() { - try { - final var plistFile = AppImageInfoPListFile.loadFromInfoPList(externalInfoPlistFile); + final var builder = new MacApplicationBuilder(this); - final var builder = new MacApplicationBuilder(this); + builder.externalInfoPlistFile(null); - builder.externalInfoPlistFile(null); + Result plistResult = Result.of(() -> { + return new PListReader(Files.readAllBytes(externalInfoPlistFile)); + }, Exception.class); + plistResult.value().ifPresent(plist -> { if (builder.bundleName == null) { - builder.bundleName(plistFile.bundleName()); + plist.findValue("CFBundleName").ifPresent(builder::bundleName); } if (builder.bundleIdentifier == null) { - builder.bundleIdentifier(plistFile.bundleIdentifier()); + plist.findValue("CFBundleIdentifier").ifPresent(builder::bundleIdentifier); } if (builder.category == null) { - builder.category(plistFile.category()); + plist.findValue("LSApplicationCategoryType").ifPresent(builder::category); } - return builder; - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } catch (Exception ex) { - throw new JPackageException( - I18N.format("error.invalid-app-image-plist-file", externalInfoPlistFile), ex); - } + if (builder.superBuilder.version().isEmpty()) { + plist.findValue("CFBundleVersion").ifPresent(builder.superBuilder::version); + } + }); + + plistResult.firstError().filter(_ -> { + // If we are building a runtime and the Info.plist file of the predefined + // runtime bundle is malformed or unavailable, ignore it. + return !superBuilder.isRuntime(); + }).ifPresent(ex -> { + // We are building an application from the predefined app image and + // the Info.plist file in the predefined app image bundle is malformed or unavailable. Bail out. + switch (ex) { + case IOException ioex -> { + throw new UncheckedIOException(ioex); + } + default -> { + throw new JPackageException( + I18N.format("error.invalid-app-image-plist-file", externalInfoPlistFile), ex); + } + } + }); + + return builder; } private Optional createSigningConfig() { return Optional.ofNullable(signingBuilder).map(AppImageSigningConfigBuilder::create); } - private String validatedBundleName() { + private String validatedBundleName(Application app) { final var value = Optional.ofNullable(bundleName).orElseGet(() -> { final var appName = app.name(); // Commented out for backward compatibility @@ -212,7 +245,7 @@ final class MacApplicationBuilder { return value; } - private String validatedBundleIdentifier() { + private String validatedBundleIdentifier(Application app) { final var value = Optional.ofNullable(bundleIdentifier).orElseGet(() -> { return app.mainLauncher() .flatMap(Launcher::startupInfo) @@ -255,7 +288,7 @@ final class MacApplicationBuilder { private Path externalInfoPlistFile; private AppImageSigningConfigBuilder signingBuilder; - private final Application app; + private final ApplicationBuilder superBuilder; private static final Defaults DEFAULTS = new Defaults("utilities"); diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java index 4cd4f386ad8..2c247ed3989 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java @@ -27,11 +27,8 @@ package jdk.jpackage.internal; import static jdk.jpackage.internal.FromOptions.buildApplicationBuilder; import static jdk.jpackage.internal.FromOptions.createPackageBuilder; import static jdk.jpackage.internal.MacPackagingPipeline.APPLICATION_LAYOUT; -import static jdk.jpackage.internal.MacRuntimeValidator.validateRuntimeHasJliLib; -import static jdk.jpackage.internal.MacRuntimeValidator.validateRuntimeHasNoBinDir; import static jdk.jpackage.internal.OptionUtils.isBundlingOperation; import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_MAC_PKG; -import static jdk.jpackage.internal.cli.StandardBundlingOperation.SIGN_MAC_APP_IMAGE; import static jdk.jpackage.internal.cli.StandardOption.APPCLASS; import static jdk.jpackage.internal.cli.StandardOption.ICON; import static jdk.jpackage.internal.cli.StandardOption.MAC_APP_CATEGORY; @@ -65,6 +62,7 @@ import jdk.jpackage.internal.cli.OptionValue; import jdk.jpackage.internal.cli.Options; import jdk.jpackage.internal.cli.StandardFaOption; import jdk.jpackage.internal.model.ApplicationLaunchers; +import jdk.jpackage.internal.model.DottedVersion; import jdk.jpackage.internal.model.ExternalApplication; import jdk.jpackage.internal.model.FileAssociation; import jdk.jpackage.internal.model.Launcher; @@ -198,17 +196,19 @@ final class MacFromOptions { } } - private static ApplicationWithDetails createMacApplicationInternal(Options options) { + private static ApplicationBuilder createApplicationBuilder(Options options) { final var predefinedRuntimeLayout = PREDEFINED_RUNTIME_IMAGE.findIn(options) .map(MacPackage::guessRuntimeLayout); - predefinedRuntimeLayout.ifPresent(layout -> { - validateRuntimeHasJliLib(layout); - if (MAC_APP_STORE.containsIn(options)) { - validateRuntimeHasNoBinDir(layout); - } - }); + predefinedRuntimeLayout.ifPresent(MacRuntimeValidator::validateRuntimeHasJliLib); + + if (MAC_APP_STORE.containsIn(options)) { + PREDEFINED_APP_IMAGE.findIn(options) + .map(APPLICATION_LAYOUT::resolveAt) + .ifPresent(MacRuntimeValidator::validateRuntimeHasNoBinDir); + predefinedRuntimeLayout.ifPresent(MacRuntimeValidator::validateRuntimeHasNoBinDir); + } final var launcherFromOptions = new LauncherFromOptions().faMapper(MacFromOptions::createMacFa); @@ -234,14 +234,32 @@ final class MacFromOptions { superAppBuilder.launchers(new ApplicationLaunchers(MacLauncher.create(mainLauncher), launchers.additionalLaunchers())); } - final var app = superAppBuilder.create(); + superAppBuilder.derivedVersionNormalizer(MacFromOptions::normalizeVersion); - final var appBuilder = new MacApplicationBuilder(app); + return superAppBuilder; + } - PREDEFINED_APP_IMAGE.findIn(options) - .map(MacBundle::new) - .map(MacBundle::infoPlistFile) - .ifPresent(appBuilder::externalInfoPlistFile); + private static ApplicationWithDetails createMacApplicationInternal(Options options) { + + final var appBuilder = new MacApplicationBuilder(createApplicationBuilder(options)); + + if (OptionUtils.isRuntimeInstaller(options)) { + // Predefined runtime image, if specified, can be a macOS bundle or regular directory. + // Notify application builder with the path to the plist file in the predefined runtime image only if the file exists. + // If it doesn't, jpackage should keep going. + PREDEFINED_RUNTIME_IMAGE.findIn(options) + .flatMap(MacBundle::fromPath) + .map(MacBundle::infoPlistFile) + .ifPresent(appBuilder::externalInfoPlistFile); + } else { + // Predefined app image, if specified, should always be a valid macOS bundle. + // Notify application builder with the path to the plist file in the predefined app image without checking if the file exists. + // If it doesn't, the builder should throw and jpackage should exit with error. + PREDEFINED_APP_IMAGE.findIn(options) + .map(MacBundle::new) + .map(MacBundle::infoPlistFile) + .ifPresent(appBuilder::externalInfoPlistFile); + } ICON.ifPresentIn(options, appBuilder::icon); MAC_BUNDLE_NAME.ifPresentIn(options, appBuilder::bundleName); @@ -251,11 +269,13 @@ final class MacFromOptions { final boolean sign = MAC_SIGN.getFrom(options); final boolean appStore; - if (PREDEFINED_APP_IMAGE.containsIn(options)) { - final var appImageFileOptions = superAppBuilder.externalApplication().orElseThrow().extra(); + if (MAC_APP_STORE.containsIn(options)) { + appStore = MAC_APP_STORE.getFrom(options); + } else if (PREDEFINED_APP_IMAGE.containsIn(options)) { + final var appImageFileOptions = appBuilder.externalApplication().orElseThrow().extra(); appStore = MAC_APP_STORE.getFrom(appImageFileOptions); } else { - appStore = MAC_APP_STORE.getFrom(options); + appStore = false; } appBuilder.appStore(appStore); @@ -295,7 +315,7 @@ final class MacFromOptions { signingBuilder.entitlementsResourceName("sandbox.plist"); } - app.mainLauncher().flatMap(Launcher::startupInfo).ifPresentOrElse( + appBuilder.launchers().map(ApplicationLaunchers::mainLauncher).flatMap(Launcher::startupInfo).ifPresentOrElse( signingBuilder::signingIdentifierPrefix, () -> { // Runtime installer does not have the main launcher, use @@ -310,7 +330,7 @@ final class MacFromOptions { appBuilder.signingBuilder(signingBuilder); } - return new ApplicationWithDetails(appBuilder.create(), superAppBuilder.externalApplication()); + return new ApplicationWithDetails(appBuilder.create(), appBuilder.externalApplication()); } private static MacPackageBuilder createMacPackageBuilder(Options options, ApplicationWithDetails app, PackageType type) { @@ -371,4 +391,11 @@ final class MacFromOptions { private static boolean hasPkgInstallerSignIdentity(Options options) { return options.contains(MAC_SIGNING_KEY_NAME) || options.contains(MAC_INSTALLER_SIGN_IDENTITY); } + + private static String normalizeVersion(String version) { + // macOS requires 1, 2 or 3 components version string. + // When reading from release file it can be 1 or 3 or maybe more. + // We will always normalize to 3 components if needed. + return DottedVersion.lazy(version).trim(3).toComponentsString(); + } } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java index e19f234d0d6..1049496146f 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -65,7 +65,7 @@ final class MacPackageBuilder { } private static void validatePredefinedAppImage(MacPackage pkg) { - if (pkg.predefinedAppImageSigned().orElse(false)) { + if (pkg.predefinedAppImageSigned().orElse(false) && !pkg.isRuntimeInstaller()) { pkg.predefinedAppImage().ifPresent(predefinedAppImage -> { var thePackageFile = PackageFile.getPathInAppImage(APPLICATION_LAYOUT); if (!Files.exists(predefinedAppImage.resolve(thePackageFile))) { diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java index 4e63f6db178..d75b7d0b9cd 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java @@ -500,7 +500,7 @@ final class MacPackagingPipeline { }; app.signingConfig().flatMap(AppImageSigningConfig::keychain).map(Keychain::new).ifPresentOrElse(keychain -> { - toBiConsumer(TempKeychain::withKeychain).accept(unused -> signAction.run(), keychain); + toBiConsumer(ActiveKeychainList::withKeychain).accept(unused -> signAction.run(), keychain); }, signAction); } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacRuntimeValidator.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacRuntimeValidator.java index cfab12cbcba..340078849c7 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacRuntimeValidator.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacRuntimeValidator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -30,6 +30,10 @@ import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.function.Predicate; +import jdk.jpackage.internal.model.AppImageLayout; +import jdk.jpackage.internal.model.ApplicationLayout; +import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.RuntimeLayout; final class MacRuntimeValidator { @@ -45,17 +49,29 @@ final class MacRuntimeValidator { throw new UncheckedIOException(ex); } - throw I18N.buildConfigException("error.invalid-runtime-image-missing-file", + throw new JPackageException(I18N.format("error.invalid-runtime-image-missing-file", runtimeLayout.rootDirectory(), - runtimeLayout.unresolve().runtimeDirectory().resolve("lib/**").resolve(jliName)).create(); + runtimeLayout.unresolve().runtimeDirectory().resolve("lib/**").resolve(jliName))); } - static void validateRuntimeHasNoBinDir(RuntimeLayout runtimeLayout) { - if (Files.isDirectory(runtimeLayout.runtimeDirectory().resolve("bin"))) { - throw I18N.buildConfigException() - .message("error.invalid-runtime-image-bin-dir", runtimeLayout.rootDirectory()) - .advice("error.invalid-runtime-image-bin-dir.advice", "--mac-app-store") - .create(); + static void validateRuntimeHasNoBinDir(AppImageLayout appImageLayout) { + if (Files.isDirectory(appImageLayout.runtimeDirectory().resolve("bin"))) { + switch (appImageLayout) { + case RuntimeLayout runtimeLayout -> { + throw new ConfigException( + I18N.format("error.invalid-runtime-image-bin-dir", runtimeLayout.rootDirectory()), + I18N.format("error.invalid-runtime-image-bin-dir.advice", "--mac-app-store")); + } + case ApplicationLayout appLayout -> { + throw new JPackageException(I18N.format("error.invalid-app-image-runtime-image-bin-dir", + appLayout.rootDirectory().relativize(appLayout.runtimeDirectory()), + appLayout.rootDirectory())); + } + default -> { + throw new IllegalArgumentException(); + } + } + } } } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/TempKeychain.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/TempKeychain.java deleted file mode 100644 index 2f616aafba1..00000000000 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/TempKeychain.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2025, 2026, 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. - */ - -package jdk.jpackage.internal; - -import java.io.Closeable; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.function.Consumer; -import jdk.internal.util.OSVersion; - -final class TempKeychain implements Closeable { - - static void withKeychains(Consumer> keychainConsumer, List keychains) { - - keychains.forEach(Objects::requireNonNull); - if (keychains.isEmpty() || OSVersion.current().compareTo(new OSVersion(10, 12)) < 0) { - keychainConsumer.accept(keychains); - } else { - // we need this for OS X 10.12+ - try (var tempKeychain = new TempKeychain(keychains)) { - keychainConsumer.accept(tempKeychain.keychains); - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - } - } - - static void withKeychain(Consumer keychainConsumer, Keychain keychain) { - - Objects.requireNonNull(keychainConsumer); - withKeychains(keychains -> { - keychainConsumer.accept(keychains.getFirst()); - }, List.of(keychain)); - } - - TempKeychain(List keychains) throws IOException { - this.keychains = Objects.requireNonNull(keychains); - - final var currentKeychains = Keychain.listKeychains(); - - final var currentKeychainPaths = currentKeychains.stream().map(Keychain::path).toList(); - - final var missingKeychains = keychains.stream().filter(k -> { - return !currentKeychainPaths.contains(k.path()); - }).toList(); - - if (missingKeychains.isEmpty()) { - restoreKeychainsCmd = List.of(); - } else { - List args = new ArrayList<>(); - args.add("/usr/bin/security"); - args.add("list-keychains"); - args.add("-s"); - args.addAll(currentKeychains.stream().map(Keychain::asCliArg).toList()); - - restoreKeychainsCmd = List.copyOf(args); - - args.addAll(missingKeychains.stream().map(Keychain::asCliArg).toList()); - - Executor.of(args).executeExpectSuccess(); - } - } - - List keychains() { - return keychains; - } - - @Override - public void close() throws IOException { - if (!restoreKeychainsCmd.isEmpty()) { - Executor.of(restoreKeychainsCmd).executeExpectSuccess(); - } - } - - private final List keychains; - private final List restoreKeychainsCmd; -} diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties index 90c4162b80e..95fff00152b 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties @@ -29,6 +29,7 @@ error.cert.not.found=No certificate found matching [{0}] using keychain [{1}] error.multiple.certs.found=Multiple certificates matching name [{0}] found in keychain [{1}] error.app-image.mac-sign.required=--mac-sign option is required with predefined application image and with type [app-image] error.invalid-runtime-image-missing-file=Runtime image "{0}" is missing "{1}" file +error.invalid-app-image-runtime-image-bin-dir=Runtime directory {0} in the predefined application image [{1}] should not contain "bin" folder error.invalid-runtime-image-bin-dir=Runtime image "{0}" should not contain "bin" folder error.invalid-runtime-image-bin-dir.advice=Use --strip-native-commands jlink option when generating runtime image used with {0} option error.invalid-app-image-plist-file=Invalid "{0}" file in the predefined application image diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java index 9d5407524a8..0cca59f7c19 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java @@ -36,19 +36,46 @@ import java.util.Optional; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; +import java.util.function.UnaryOperator; import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLaunchers; import jdk.jpackage.internal.model.ExternalApplication; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.LauncherIcon; +import jdk.jpackage.internal.model.LauncherModularStartupInfo; import jdk.jpackage.internal.model.LauncherStartupInfo; import jdk.jpackage.internal.model.ResourceDirLauncherIcon; import jdk.jpackage.internal.model.RuntimeBuilder; +import jdk.jpackage.internal.model.RuntimeLayout; import jdk.jpackage.internal.util.RootedPath; +import jdk.jpackage.internal.util.RuntimeReleaseFile; final class ApplicationBuilder { + ApplicationBuilder() { + } + + ApplicationBuilder(ApplicationBuilder other) { + name = other.name; + description = other.description; + version = other.version; + vendor = other.vendor; + copyright = other.copyright; + appDirSources = other.appDirSources; + externalApp = other.externalApp; + contentDirSources = other.contentDirSources; + appImageLayout = other.appImageLayout; + runtimeBuilder = other.runtimeBuilder; + launchers = other.launchers; + runtimeReleaseFile = other.runtimeReleaseFile; + derivedVersionNormalizer = other.derivedVersionNormalizer; + } + + ApplicationBuilder copy() { + return new ApplicationBuilder(this); + } + Application create() { Objects.requireNonNull(appImageLayout); @@ -64,7 +91,7 @@ final class ApplicationBuilder { return new Application.Stub( effectiveName, Optional.ofNullable(description).orElse(effectiveName), - Optional.ofNullable(version).orElseGet(DEFAULTS::version), + validatedVersion(), Optional.ofNullable(vendor).orElseGet(DEFAULTS::vendor), Optional.ofNullable(copyright).orElseGet(DEFAULTS::copyright), Optional.ofNullable(appDirSources).orElseGet(List::of), @@ -103,6 +130,11 @@ final class ApplicationBuilder { return this; } + boolean isRuntime() { + return Optional.ofNullable(appImageLayout) + .orElseThrow(IllegalStateException::new) instanceof RuntimeLayout; + } + ApplicationBuilder name(String v) { name = v; return this; @@ -118,6 +150,15 @@ final class ApplicationBuilder { return this; } + Optional version() { + return Optional.ofNullable(version); + } + + ApplicationBuilder runtimeReleaseFile(Path v) { + runtimeReleaseFile = v; + return this; + } + ApplicationBuilder vendor(String v) { vendor = v; return this; @@ -138,6 +179,54 @@ final class ApplicationBuilder { return this; } + ApplicationBuilder derivedVersionNormalizer(UnaryOperator v) { + derivedVersionNormalizer = v; + return this; + } + + private String validatedVersion() { + return Optional.ofNullable(version).or(() -> { + // Application version has not been specified explicitly. Derive it. + var derivedVersion = derivedVersion(); + if (derivedVersionNormalizer != null) { + derivedVersion = derivedVersion.map(v -> { + var mappedVersion = derivedVersionNormalizer.apply(v); + if (!mappedVersion.equals(v)) { + Log.verbose(I18N.format("message.version-normalized", mappedVersion, v)); + } + return mappedVersion; + }); + } + return derivedVersion; + }).orElseGet(DEFAULTS::version); + } + + private Optional derivedVersion() { + if (appImageLayout instanceof RuntimeLayout && runtimeReleaseFile != null) { + try { + var releaseVersion = new RuntimeReleaseFile(runtimeReleaseFile).getJavaVersion().toString(); + Log.verbose(I18N.format("message.release-version", releaseVersion)); + return Optional.of(releaseVersion); + } catch (Exception ex) { + Log.verbose(ex); + return Optional.empty(); + } + } else if (launchers != null) { + return launchers.mainLauncher().startupInfo() + .filter(LauncherModularStartupInfo.class::isInstance) + .map(LauncherModularStartupInfo.class::cast) + .flatMap(modularStartupInfo -> { + var moduleVersion = modularStartupInfo.moduleVersion(); + moduleVersion.ifPresent(v -> { + Log.verbose(I18N.format("message.module-version", v, modularStartupInfo.moduleName())); + }); + return moduleVersion; + }); + } else { + return Optional.empty(); + } + } + static ApplicationLaunchers normalizeIcons( ApplicationLaunchers appLaunchers, Optional resourceDir, BiFunction launcherOverrideCtor) { @@ -302,6 +391,8 @@ final class ApplicationBuilder { private AppImageLayout appImageLayout; private RuntimeBuilder runtimeBuilder; private ApplicationLaunchers launchers; + private Path runtimeReleaseFile; + private UnaryOperator derivedVersionNormalizer; private static final Defaults DEFAULTS = new Defaults( "1.0", diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.java index 4f590850328..4b4e6f9dbac 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.java @@ -59,10 +59,10 @@ import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLaunchers; import jdk.jpackage.internal.model.ApplicationLayout; import jdk.jpackage.internal.model.Launcher; -import jdk.jpackage.internal.model.LauncherModularStartupInfo; import jdk.jpackage.internal.model.PackageType; import jdk.jpackage.internal.model.RuntimeLayout; import jdk.jpackage.internal.util.RootedPath; +import jdk.jpackage.internal.util.RuntimeReleaseFile; final class FromOptions { @@ -212,19 +212,6 @@ final class FromOptions { runtimeBuilderBuilder.modulePath(ensureBaseModuleInModulePath(MODULE_PATH.findIn(options).orElseGet(List::of))); - if (!APP_VERSION.containsIn(options)) { - // Version is not specified explicitly. Try to get it from the app's module. - launchers.mainLauncher().startupInfo().ifPresent(startupInfo -> { - if (startupInfo instanceof LauncherModularStartupInfo modularStartupInfo) { - modularStartupInfo.moduleVersion().ifPresent(moduleVersion -> { - appBuilder.version(moduleVersion); - Log.verbose(I18N.format("message.module-version", - moduleVersion, modularStartupInfo.moduleName())); - }); - } - }); - } - predefinedRuntimeDirectory.ifPresentOrElse(runtimeBuilderBuilder::forRuntime, () -> { final var startupInfos = launchers.asList().stream() .map(Launcher::startupInfo) @@ -239,6 +226,8 @@ final class FromOptions { } } + predefinedRuntimeDirectory.map(RuntimeReleaseFile::releaseFilePathInRuntime).ifPresent(appBuilder::runtimeReleaseFile); + return appBuilder; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Globals.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Globals.java index d5c714d1a47..0128d050c25 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Globals.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Globals.java @@ -25,6 +25,9 @@ package jdk.jpackage.internal; import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.function.Supplier; @@ -47,6 +50,21 @@ public final class Globals { return objectFactory(ObjectFactory.build(objectFactory).executorFactory(v).create()); } + @SuppressWarnings("unchecked") + public Optional findProperty(Object key) { + return Optional.ofNullable((T)properties.get(Objects.requireNonNull(key))); + } + + public Optional findBooleanProperty(Object key) { + return findProperty(key); + } + + public Globals setProperty(Object key, T value) { + checkMutable(); + properties.compute(Objects.requireNonNull(key), (_, _) -> value); + return this; + } + Log.Logger logger() { return logger; } @@ -79,6 +97,7 @@ public final class Globals { private ObjectFactory objectFactory = ObjectFactory.DEFAULT; private final Log.Logger logger = new Log.Logger(); + private final Map properties = new HashMap<>(); private static final ScopedValue INSTANCE = ScopedValue.newInstance(); private static final Globals DEFAULT = new Globals(); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModuleInfo.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModuleInfo.java index 71d1a66659e..9555423db62 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModuleInfo.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModuleInfo.java @@ -24,17 +24,13 @@ */ package jdk.jpackage.internal; -import java.io.IOException; -import java.io.Reader; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleReference; import java.net.URI; -import java.nio.file.Files; import java.nio.file.Path; -import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.Properties; +import jdk.jpackage.internal.util.RuntimeReleaseFile; record ModuleInfo(String name, Optional version, Optional mainClass, Optional location) { @@ -56,30 +52,16 @@ record ModuleInfo(String name, Optional version, Optional mainCl // We can't extract info about version and main class of a module // linked in external runtime without running ModuleFinder in that // runtime. But this is too much work as the runtime might have been - // coocked without native launchers. So just make sure the module - // is linked in the runtime by simply analysing the data + // cooked without native launchers. So just make sure the module + // is linked in the runtime by simply analyzing the data // of `release` file. - final Path releaseFile = cookedRuntime.resolve("release"); - - try (Reader reader = Files.newBufferedReader(releaseFile)) { - Properties props = new Properties(); - props.load(reader); - String moduleList = props.getProperty("MODULES"); - if (moduleList == null) { + try { + var cookedRuntimeModules = RuntimeReleaseFile.loadFromRuntime(cookedRuntime).getModules(); + if (!cookedRuntimeModules.contains(moduleName)) { return Optional.empty(); } - - if ((moduleList.startsWith("\"") && moduleList.endsWith("\"")) - || (moduleList.startsWith("\'") && moduleList.endsWith( - "\'"))) { - moduleList = moduleList.substring(1, moduleList.length() - 1); - } - - if (!List.of(moduleList.split("\\s+")).contains(moduleName)) { - return Optional.empty(); - } - } catch (IOException|IllegalArgumentException ex) { + } catch (Exception ex) { Log.verbose(ex); return Optional.empty(); } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java index f554fc12ec6..438a5ac3e6f 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java @@ -342,7 +342,7 @@ public final class StandardOption { public static final OptionValue MAC_SIGN = booleanOption("mac-sign").scope(MAC_SIGNING).addAliases("s").create(); - public static final OptionValue MAC_APP_STORE = booleanOption("mac-app-store").create(); + public static final OptionValue MAC_APP_STORE = booleanOption("mac-app-store").scope(MAC_SIGNING).create(); public static final OptionValue MAC_APP_CATEGORY = stringOption("mac-app-category").create(); @@ -400,6 +400,8 @@ public final class StandardOption { public static final OptionValue WIN_INSTALLDIR_CHOOSER = booleanOption("win-dir-chooser").scope(nativeBundling()).create(); + public static final OptionValue WIN_WITH_UI = booleanOption("win-with-ui").scope(nativeBundling()).create(); + public static final OptionValue WIN_UPGRADE_UUID = uuidOption("win-upgrade-uuid").scope(nativeBundling()).create(); public static final OptionValue WIN_CONSOLE_HINT = booleanOption("win-console") diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties index 466f58ee68e..0b2ca83a7d7 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2017, 2026, 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 @@ -426,3 +426,5 @@ help.option.win-update-url=\ help.option.win-upgrade-uuid=\ \ UUID associated with upgrades for this package +help.option.win-with-ui=\ +\ Enforces the installer to have UI diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties index 233067d6457..02784a52acc 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties @@ -52,6 +52,8 @@ message.app-image-created=Succeeded in building output application image directo message.debug-working-directory=Kept working directory for debug: {0} message.module-version=Using version "{0}" from module "{1}" as application version +message.release-version=Using version "{0}" from "release" file of the predefined runtime as a package version +message.version-normalized=Using version "{0}" normalized to platform supported format from "{1}" message.error-header=Error: {0} message.advice-header=Advice to fix: {0} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RuntimeReleaseFile.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RuntimeReleaseFile.java new file mode 100644 index 00000000000..e055417f391 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RuntimeReleaseFile.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2026, 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. + */ + +package jdk.jpackage.internal.util; + +import java.io.IOException; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.Properties; + +public final class RuntimeReleaseFile { + + public RuntimeReleaseFile(Path releaseFilePath) throws IOException { + // The implementation is based on the behavior of + // jdk.tools.jlink.internal.plugins.ReleaseInfoPlugin, which + // uses java.util.Properties to read/write the "release" file. + try (Reader reader = Files.newBufferedReader(releaseFilePath)) { + props = new Properties(); + props.load(reader); + } + } + + /** + * Returns path to the "runtime" file in the specified runtime directory. + * + * @param runtimeDir the path to a directory with the standard Java runtime + * structure + */ + public static Path releaseFilePathInRuntime(Path runtimeDir) { + return runtimeDir.resolve("release"); + } + + /** + * Creates the instance form the "runtime" file in the specified runtime + * directory. + *

+ * Uses {@link #releaseFilePathInRuntime(Path)} to get the path to the "runtime" + * file in the specified runtime directory. + * + * @param runtimeDir the path to a directory with the standard Java runtime + * structure + */ + public static RuntimeReleaseFile loadFromRuntime(Path runtimeDir) throws IOException { + return new RuntimeReleaseFile(releaseFilePathInRuntime(runtimeDir)); + } + + /** + * Returns verbatim value of the property with the specified name or an empty + * {@code Optional} if there is no property with the specified name. + *

+ * Property values in the "release" file are enclosed in double quotes. + * This method returns the value with the double quotes. + * + * @param propertyName the property name + */ + public Optional findRawProperty(String propertyName) { + return Optional.ofNullable(props.getProperty(propertyName)); + } + + /** + * Returns unquoted value of the property with the specified name or an empty + * {@code Optional} if there is no property with the specified name. + *

+ * Property values in the "release" file are enclosed in double quotes. This + * method returns the value without the double quotes. + * + * @param propertyName the property name + */ + public Optional findProperty(String propertyName) { + return findRawProperty(propertyName).map(v -> { + if (v.charAt(0) == '"' && v.charAt(v.length() - 1) == '"') { + return v.substring(1, v.length() - 1); + } else { + return v; + } + }); + } + + /** + * Returns the value of the "JAVA_VERSION" property. + *

+ * Will throw {@code NoSuchElementException} if there is no such property. Will + * use {@link Runtime.Version#parse(String)} method to parse version string. Any + * exception that it may yield will be passed to the caller verbatim. + * + * @throws NoSuchElementException if there is no such property + */ + public Runtime.Version getJavaVersion() { + return findProperty("JAVA_VERSION").map(Runtime.Version::parse).orElseThrow(NoSuchElementException::new); + } + + /** + * Returns the value of the "MODULES" property. + * + * @throws NoSuchElementException if there is no such property + */ + public List getModules() { + return findProperty("MODULES").map(v -> { + return List.of(v.split("\\s+")); + }).orElseThrow(NoSuchElementException::new); + } + + private final Properties props; +} diff --git a/src/jdk.jpackage/share/man/jpackage.md b/src/jdk.jpackage/share/man/jpackage.md index f78bec9808c..9ba5949866f 100644 --- a/src/jdk.jpackage/share/man/jpackage.md +++ b/src/jdk.jpackage/share/man/jpackage.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018, 2026, 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 @@ -436,6 +436,10 @@ The `jpackage` tool will take as input a Java application and a Java run-time im : UUID associated with upgrades for this package +`--win-with-ui` + +: Enforces the installer to have UI + #### Linux platform options (available only when running on Linux): `--linux-package-name` *name* diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/MsiMutator.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/MsiMutator.java new file mode 100644 index 00000000000..592cc55c0df --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/MsiMutator.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021, 2026, 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. + */ +package jdk.jpackage.internal; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import jdk.jpackage.internal.resources.ResourceLocator; + +/** + * WSH script altering cooked .msi file. + */ +record MsiMutator(String scriptResourceName) { + + MsiMutator { + Objects.requireNonNull(scriptResourceName); + if (Path.of(scriptResourceName).getNameCount() != 1) { + throw new IllegalArgumentException(); + } + } + + void addToConfigRoot(Path configRoot) throws IOException { + var scriptFile = configRoot.resolve(pathInConfigRoot()); + try (var in = ResourceLocator.class.getResourceAsStream(scriptResourceName)) { + Files.createDirectories(scriptFile.getParent()); + Files.copy(in, scriptFile); + } + } + + Path pathInConfigRoot() { + return Path.of(scriptResourceName); + } +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOptions.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOptions.java index 6009e2f6724..59701777396 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOptions.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -39,9 +39,11 @@ import static jdk.jpackage.internal.cli.StandardOption.WIN_SHORTCUT_HINT; import static jdk.jpackage.internal.cli.StandardOption.WIN_SHORTCUT_PROMPT; import static jdk.jpackage.internal.cli.StandardOption.WIN_UPDATE_URL; import static jdk.jpackage.internal.cli.StandardOption.WIN_UPGRADE_UUID; +import static jdk.jpackage.internal.cli.StandardOption.WIN_WITH_UI; import static jdk.jpackage.internal.model.StandardPackageType.WIN_MSI; import jdk.jpackage.internal.cli.Options; +import jdk.jpackage.internal.model.DottedVersion; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.WinApplication; import jdk.jpackage.internal.model.WinExePackage; @@ -73,6 +75,8 @@ final class WinFromOptions { appBuilder.launchers().map(WinPackagingPipeline::normalizeShortcuts).ifPresent(appBuilder::launchers); + appBuilder.derivedVersionNormalizer(WinFromOptions::normalizeVersion); + return WinApplication.create(appBuilder.create()); } @@ -90,6 +94,7 @@ final class WinFromOptions { WIN_UPDATE_URL.ifPresentIn(options, pkgBuilder::updateURL); WIN_INSTALLDIR_CHOOSER.ifPresentIn(options, pkgBuilder::withInstallDirChooser); WIN_SHORTCUT_PROMPT.ifPresentIn(options, pkgBuilder::withShortcutPrompt); + WIN_WITH_UI.ifPresentIn(options, pkgBuilder::withUi); if (app.isService()) { RESOURCE_DIR.ifPresentIn(options, resourceDir -> { @@ -112,4 +117,11 @@ final class WinFromOptions { return pkgBuilder.create(); } + + private static String normalizeVersion(String version) { + // Windows requires between 2 and 4 components version string. + // When reading from release file it can be 1 or 3 or maybe more. + // One component will be normalized to 2 and more then 4 will be trim to 4. + return DottedVersion.lazy(version).trim(4).pad(2).toComponentsString(); + } } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackageBuilder.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackageBuilder.java index 7695cf04ac1..c2564028ecb 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackageBuilder.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackageBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -65,6 +65,7 @@ final class WinMsiPackageBuilder { MsiVersion.of(pkg.version()), withInstallDirChooser, withShortcutPrompt, + withUi, Optional.ofNullable(helpURL), Optional.ofNullable(updateURL), Optional.ofNullable(startMenuGroupName).orElseGet(DEFAULTS::startMenuGroupName), @@ -92,6 +93,11 @@ final class WinMsiPackageBuilder { return this; } + WinMsiPackageBuilder withUi(boolean v) { + withUi = v; + return this; + } + WinMsiPackageBuilder helpURL(String v) { helpURL = v; return this; @@ -131,6 +137,7 @@ final class WinMsiPackageBuilder { private boolean withInstallDirChooser; private boolean withShortcutPrompt; + private boolean withUi; private String helpURL; private String updateURL; private String startMenuGroupName; diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java index a53d083847a..3eb7b10b846 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java @@ -36,7 +36,6 @@ import java.nio.file.Path; import java.nio.file.PathMatcher; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -315,50 +314,50 @@ final class WinMsiPackager implements Consumer { wixPipeline.buildMsi(msiOut.toAbsolutePath()); } - private Map createWixVars() throws IOException { - Map data = new HashMap<>(); + private WixVariables createWixVars() throws IOException { + var wixVars = new WixVariables(); - data.put("JpProductCode", pkg.productCode().toString()); - data.put("JpProductUpgradeCode", pkg.upgradeCode().toString()); + wixVars.put("JpProductCode", pkg.productCode().toString()); + wixVars.put("JpProductUpgradeCode", pkg.upgradeCode().toString()); Log.verbose(I18N.format("message.product-code", pkg.productCode())); Log.verbose(I18N.format("message.upgrade-code", pkg.upgradeCode())); - data.put("JpAllowUpgrades", "yes"); + wixVars.define("JpAllowUpgrades"); if (!pkg.isRuntimeInstaller()) { - data.put("JpAllowDowngrades", "yes"); + wixVars.define("JpAllowDowngrades"); } - data.put("JpAppName", pkg.packageName()); - data.put("JpAppDescription", pkg.description()); - data.put("JpAppVendor", pkg.app().vendor()); - data.put("JpAppVersion", pkg.version()); + wixVars.put("JpAppName", pkg.packageName()); + wixVars.put("JpAppDescription", pkg.description()); + wixVars.put("JpAppVendor", pkg.app().vendor()); + wixVars.put("JpAppVersion", pkg.version()); if (Files.exists(installerIcon)) { - data.put("JpIcon", installerIcon.toString()); + wixVars.put("JpIcon", installerIcon.toString()); } pkg.helpURL().ifPresent(value -> { - data.put("JpHelpURL", value); + wixVars.put("JpHelpURL", value); }); pkg.updateURL().ifPresent(value -> { - data.put("JpUpdateURL", value); + wixVars.put("JpUpdateURL", value); }); pkg.aboutURL().ifPresent(value -> { - data.put("JpAboutURL", value); + wixVars.put("JpAboutURL", value); }); - data.put("JpAppSizeKb", Long.toString(AppImageLayout.toPathGroup( + wixVars.put("JpAppSizeKb", Long.toString(AppImageLayout.toPathGroup( env.appImageLayout()).sizeInBytes() >> 10)); - data.put("JpConfigDir", env.configDir().toAbsolutePath().toString()); + wixVars.put("JpConfigDir", env.configDir().toAbsolutePath().toString()); if (pkg.isSystemWideInstall()) { - data.put("JpIsSystemWide", "yes"); + wixVars.define("JpIsSystemWide"); } - return data; + return wixVars; } private static List getWxlFilesFromDir(Path dir) { diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixFragmentBuilder.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixFragmentBuilder.java index 46894699d98..c842c366f63 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixFragmentBuilder.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixFragmentBuilder.java @@ -32,7 +32,6 @@ import java.nio.file.Path; import java.util.Collection; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.stream.XMLStreamWriter; @@ -65,16 +64,14 @@ abstract class WixFragmentBuilder { } void initFromParams(BuildEnv env, WinMsiPackage pkg) { - wixVariables = null; + wixVariables = new WixVariables(); additionalResources = null; configRoot = env.configDir(); fragmentResource = env.createResource(defaultResourceName).setPublicName(outputFileName); } void configureWixPipeline(WixPipeline.Builder wixPipeline) { - wixPipeline.addSource(configRoot.resolve(outputFileName), - Optional.ofNullable(wixVariables).map(WixVariables::getValues).orElse( - null)); + wixPipeline.addSource(configRoot.resolve(outputFileName), wixVariables); } void addFilesToConfigRoot() throws IOException { @@ -147,14 +144,11 @@ abstract class WixFragmentBuilder { protected abstract Collection getFragmentWriters(); protected final void defineWixVariable(String variableName) { - setWixVariable(variableName, "yes"); + wixVariables.define(variableName); } protected final void setWixVariable(String variableName, String variableValue) { - if (wixVariables == null) { - wixVariables = new WixVariables(); - } - wixVariables.setWixVariable(variableName, variableValue); + wixVariables.put(variableName, variableValue); } protected final void addResource(OverridableResource resource, String saveAsName) { diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixPipeline.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixPipeline.java index 40160192862..a09db389ca5 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixPipeline.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixPipeline.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -24,22 +24,18 @@ */ package jdk.jpackage.internal; +import static jdk.jpackage.internal.ShortPathUtils.adjustPath; + import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Set; -import java.util.function.Function; +import java.util.function.Consumer; import java.util.function.UnaryOperator; -import java.util.stream.Collectors; import java.util.stream.Stream; -import static jdk.jpackage.internal.ShortPathUtils.adjustPath; import jdk.jpackage.internal.util.PathUtils; /** @@ -61,18 +57,20 @@ final class WixPipeline { final var absWorkDir = workDir.normalize().toAbsolutePath(); - final UnaryOperator normalizePath = path -> { - return path.normalize().toAbsolutePath(); - }; + final var absObjWorkDir = PathUtils.normalizedAbsolutePath(wixObjDir); - final var absObjWorkDir = normalizePath.apply(wixObjDir); - - var relSources = sources.stream().map(source -> { - return source.overridePath(normalizePath.apply(source.path)); + final var absSources = sources.stream().map(source -> { + return source.copyWithPath(PathUtils.normalizedAbsolutePath(source.path)); }).toList(); - return new WixPipeline(toolset, adjustPath(absWorkDir), absObjWorkDir, - wixVariables, mapLightOptions(normalizePath), relSources); + return new WixPipeline( + toolset, + adjustPath(absWorkDir), + absObjWorkDir, + wixVariables.createdImmutableCopy(), + mapLightOptions(PathUtils::normalizedAbsolutePath), + absSources, + msiMutators); } Builder setWixObjDir(Path v) { @@ -85,17 +83,30 @@ final class WixPipeline { return this; } - Builder setWixVariables(Map v) { - wixVariables.clear(); + Builder putWixVariables(WixVariables v) { wixVariables.putAll(v); return this; } - Builder addSource(Path source, Map wixVariables) { - sources.add(new WixSource(source, wixVariables)); + Builder putWixVariables(Map v) { + wixVariables.putAll(v); return this; } + Builder addSource(Path source, WixVariables wixVariables) { + sources.add(new WixSource(source, wixVariables.createdImmutableCopy())); + return this; + } + + Builder addMsiMutator(MsiMutator msiMutator, List args) { + msiMutators.add(new MsiMutatorWithArgs(msiMutator, args)); + return this; + } + + Builder addSource(Path source) { + return addSource(source, WixVariables.EMPTY); + } + Builder addLightOptions(String ... v) { lightOptions.addAll(List.of(v)); return this; @@ -119,87 +130,59 @@ final class WixPipeline { private Path workDir; private Path wixObjDir; - private final Map wixVariables = new HashMap<>(); + private final WixVariables wixVariables = new WixVariables(); private final List lightOptions = new ArrayList<>(); private final List sources = new ArrayList<>(); + private final List msiMutators = new ArrayList<>(); } static Builder build() { return new Builder(); } - private WixPipeline(WixToolset toolset, Path workDir, Path wixObjDir, - Map wixVariables, List lightOptions, - List sources) { - this.toolset = toolset; - this.workDir = workDir; - this.wixObjDir = wixObjDir; - this.wixVariables = wixVariables; - this.lightOptions = lightOptions; - this.sources = sources; + private WixPipeline( + WixToolset toolset, + Path workDir, + Path wixObjDir, + WixVariables wixVariables, + List lightOptions, + List sources, + List msiMutators) { + + this.toolset = Objects.requireNonNull(toolset); + this.workDir = Objects.requireNonNull(workDir); + this.wixObjDir = Objects.requireNonNull(wixObjDir); + this.wixVariables = Objects.requireNonNull(wixVariables); + this.lightOptions = Objects.requireNonNull(lightOptions); + this.sources = Objects.requireNonNull(sources); + this.msiMutators = Objects.requireNonNull(msiMutators); } void buildMsi(Path msi) throws IOException { - Objects.requireNonNull(workDir); // Use short path to the output msi to workaround // WiX limitations of handling long paths. var transientMsi = wixObjDir.resolve("a.msi"); + var configRoot = workDir.resolve(transientMsi).getParent(); + + for (var msiMutator : msiMutators) { + msiMutator.addToConfigRoot(configRoot); + } + switch (toolset.getType()) { case Wix3 -> buildMsiWix3(transientMsi); case Wix4 -> buildMsiWix4(transientMsi); - default -> throw new IllegalArgumentException(); + } + + for (var msiMutator : msiMutators) { + msiMutator.execute(configRoot, workDir.resolve(transientMsi)); } IOUtils.copyFile(workDir.resolve(transientMsi), msi); } - private void addWixVariblesToCommandLine( - Map otherWixVariables, List cmdline) { - Stream.of(wixVariables, Optional.ofNullable(otherWixVariables). - orElseGet(Collections::emptyMap)).filter(Objects::nonNull). - reduce((a, b) -> { - a.putAll(b); - return a; - }).ifPresent(wixVars -> { - var entryStream = wixVars.entrySet().stream(); - - Stream stream; - switch (toolset.getType()) { - case Wix3 -> { - stream = entryStream.map(wixVar -> { - return String.format("-d%s=%s", wixVar.getKey(), wixVar. - getValue()); - }); - } - case Wix4 -> { - stream = entryStream.map(wixVar -> { - return Stream.of("-d", String.format("%s=%s", wixVar. - getKey(), wixVar.getValue())); - }).flatMap(Function.identity()); - } - default -> { - throw new IllegalArgumentException(); - } - } - - stream.reduce(cmdline, (ctnr, wixVar) -> { - ctnr.add(wixVar); - return ctnr; - }, (x, y) -> { - x.addAll(y); - return x; - }); - }); - } - private void buildMsiWix4(Path msi) throws IOException { - var mergedSrcWixVars = sources.stream().map(wixSource -> { - return Optional.ofNullable(wixSource.variables).orElseGet( - Collections::emptyMap).entrySet().stream(); - }).flatMap(Function.identity()).collect(Collectors.toMap( - Map.Entry::getKey, Map.Entry::getValue)); List cmdline = new ArrayList<>(List.of( toolset.getToolPath(WixTool.Wix4).toString(), @@ -213,7 +196,7 @@ final class WixPipeline { cmdline.addAll(lightOptions); - addWixVariblesToCommandLine(mergedSrcWixVars, cmdline); + addWixVariablesToCommandLine(sources.stream(), cmdline::addAll); cmdline.addAll(sources.stream().map(wixSource -> { return wixSource.path.toString(); @@ -241,7 +224,6 @@ final class WixPipeline { lightCmdline.addAll(lightOptions); wixObjs.stream().map(Path::toString).forEach(lightCmdline::add); - Files.createDirectories(msi.getParent()); execute(lightCmdline); } @@ -262,7 +244,7 @@ final class WixPipeline { cmdline.add("-fips"); } - addWixVariblesToCommandLine(wixSource.variables, cmdline); + addWixVariablesToCommandLine(Stream.of(wixSource), cmdline::addAll); execute(cmdline); @@ -273,16 +255,47 @@ final class WixPipeline { Executor.of(new ProcessBuilder(cmdline).directory(workDir.toFile())).executeExpectSuccess(); } - private record WixSource(Path path, Map variables) { - WixSource overridePath(Path path) { + private void addWixVariablesToCommandLine(Stream wixSources, Consumer> sink) { + sink.accept(wixSources.map(WixSource::variables).reduce(wixVariables, (a, b) -> { + return new WixVariables().putAll(a).putAll(b); + }).toWixCommandLine(toolset.getType())); + } + + private record WixSource(Path path, WixVariables variables) { + WixSource { + Objects.requireNonNull(path); + Objects.requireNonNull(variables); + } + + WixSource copyWithPath(Path path) { return new WixSource(path, variables); } } + private record MsiMutatorWithArgs(MsiMutator mutator, List args) { + MsiMutatorWithArgs { + Objects.requireNonNull(mutator); + Objects.requireNonNull(args); + } + + void addToConfigRoot(Path configRoot) throws IOException { + mutator.addToConfigRoot(configRoot); + } + + void execute(Path configRoot, Path transientMsi) throws IOException { + Executor.of("cscript", "//Nologo") + .args(PathUtils.normalizedAbsolutePathString(configRoot.resolve(mutator.pathInConfigRoot()))) + .args(PathUtils.normalizedAbsolutePathString(transientMsi)) + .args(args) + .executeExpectSuccess(); + } + } + private final WixToolset toolset; - private final Map wixVariables; + private final WixVariables wixVariables; private final List lightOptions; private final Path wixObjDir; private final Path workDir; private final List sources; + private final List msiMutators; } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixUiFragmentBuilder.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixUiFragmentBuilder.java index 4a2a0756dbd..4748dcfb827 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixUiFragmentBuilder.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixUiFragmentBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -24,25 +24,32 @@ */ package jdk.jpackage.internal; -import jdk.jpackage.internal.model.WinMsiPackage; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.function.Function; -import java.util.function.Supplier; +import java.util.stream.Stream; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import jdk.jpackage.internal.WixAppImageFragmentBuilder.ShortcutsFolder; import jdk.jpackage.internal.WixToolset.WixToolsetType; +import jdk.jpackage.internal.model.WinMsiPackage; import jdk.jpackage.internal.resources.ResourceLocator; import jdk.jpackage.internal.util.XmlConsumer; +import jdk.jpackage.internal.wixui.Dialog; +import jdk.jpackage.internal.wixui.DialogPair; +import jdk.jpackage.internal.wixui.Publish; +import jdk.jpackage.internal.wixui.ShowActionSuppresser; +import jdk.jpackage.internal.wixui.UIConfig; +import jdk.jpackage.internal.wixui.UISpec; /** * Creates UI WiX fragment. @@ -53,63 +60,83 @@ final class WixUiFragmentBuilder extends WixFragmentBuilder { void initFromParams(BuildEnv env, WinMsiPackage pkg) { super.initFromParams(env, pkg); - withLicenseDlg = pkg.licenseFile().isPresent(); - if (withLicenseDlg) { + final var shortcutFolders = ShortcutsFolder.getForPackage(pkg); + + uiConfig = UIConfig.build() + .withLicenseDlg(pkg.licenseFile().isPresent()) + .withInstallDirChooserDlg(pkg.withInstallDirChooser()) + .withShortcutPromptDlg(!shortcutFolders.isEmpty() && pkg.withShortcutPrompt()) + .create(); + + if (!uiConfig.equals(UIConfig.build().create()) || pkg.withUI()) { + uiSpec = Optional.of(UISpec.create(uiConfig)); + } else { + uiSpec = Optional.empty(); + } + + if (uiConfig.isWithLicenseDlg()) { Path licenseFileName = pkg.licenseFile().orElseThrow().getFileName(); Path destFile = getConfigRoot().resolve(licenseFileName); setWixVariable("JpLicenseRtf", destFile.toAbsolutePath().toString()); } - withInstallDirChooserDlg = pkg.withInstallDirChooser(); - - final var shortcutFolders = ShortcutsFolder.getForPackage(pkg); - - withShortcutPromptDlg = !shortcutFolders.isEmpty() && pkg.withShortcutPrompt(); - customDialogs = new ArrayList<>(); - if (withShortcutPromptDlg) { - CustomDialog dialog = new CustomDialog(env::createResource, I18N.getString( - "resource.shortcutpromptdlg-wix-file"), + if (uiConfig.isWithShortcutPromptDlg()) { + CustomDialog dialog = new CustomDialog( + env::createResource, + I18N.getString("resource.shortcutpromptdlg-wix-file"), "ShortcutPromptDlg.wxs"); for (var shortcutFolder : shortcutFolders) { - dialog.wixVariables.defineWixVariable( + dialog.wixVariables.define( shortcutFolder.getWixVariableName()); } customDialogs.add(dialog); } - if (withInstallDirChooserDlg) { - CustomDialog dialog = new CustomDialog(env::createResource, I18N.getString( - "resource.installdirnotemptydlg-wix-file"), + if (uiConfig.isWithInstallDirChooserDlg()) { + CustomDialog dialog = new CustomDialog( + env::createResource, + I18N.getString("resource.installdirnotemptydlg-wix-file"), "InstallDirNotEmptyDlg.wxs"); - List

dialogIds = getUI().dialogIdsSupplier.apply(this); - dialog.wixVariables.setWixVariable("JpAfterInstallDirDlg", - dialogIds.get(dialogIds.indexOf(Dialog.InstallDirDlg) + 1).id); customDialogs.add(dialog); } + } @Override void configureWixPipeline(WixPipeline.Builder wixPipeline) { super.configureWixPipeline(wixPipeline); - if (withShortcutPromptDlg || withInstallDirChooserDlg || withLicenseDlg) { - final String extName; - switch (getWixType()) { - case Wix3 -> extName = "WixUIExtension"; - case Wix4 -> extName = "WixToolset.UI.wixext"; - default -> throw new IllegalArgumentException(); - } - wixPipeline.addLightOptions("-ext", extName); - } - // Only needed if we using CA dll, so Wix can find it if (withCustomActionsDll) { wixPipeline.addLightOptions("-b", getConfigRoot().toAbsolutePath().toString()); } + if (uiSpec.isEmpty()) { + return; + } + + var extName = switch (getWixType()) { + case Wix3 -> "WixUIExtension"; + case Wix4 -> "WixToolset.UI.wixext"; + }; + wixPipeline.addLightOptions("-ext", extName); + wixPipeline.putWixVariables(uiSpec.get().wixVariables()); + + if (!uiSpec.get().hideDialogs().isEmpty() && getWixType() == WixToolsetType.Wix3) { + // Older WiX doesn't support multiple overrides of a "ShowAction" element. + // Have to run a script to alter the msi. + var removeActions = uiSpec.get().hideDialogs().stream() + .map(ShowActionSuppresser::dialog) + .sorted(Dialog.DEFAULT_COMPARATOR) + .map(Dialog::id); + wixPipeline.addMsiMutator( + new MsiMutator("msi-disable-actions.js"), + Stream.concat(Stream.of("InstallUISequence"), removeActions).toList()); + } + for (var customDialog : customDialogs) { customDialog.addToWixPipeline(wixPipeline); } @@ -132,26 +159,24 @@ final class WixUiFragmentBuilder extends WixFragmentBuilder { return List.of(this::addUI); } - private void addUI(XMLStreamWriter xml) throws XMLStreamException, - IOException { + private void addUI(XMLStreamWriter xml) throws XMLStreamException, IOException { - if (withInstallDirChooserDlg) { + if (uiConfig.isWithInstallDirChooserDlg()) { xml.writeStartElement("Property"); xml.writeAttribute("Id", "WIXUI_INSTALLDIR"); xml.writeAttribute("Value", "INSTALLDIR"); xml.writeEndElement(); // Property } - if (withLicenseDlg) { + if (uiConfig.isWithLicenseDlg()) { xml.writeStartElement("WixVariable"); xml.writeAttribute("Id", "WixUILicenseRtf"); xml.writeAttribute("Value", "$(var.JpLicenseRtf)"); xml.writeEndElement(); // WixVariable } - var ui = getUI(); - if (ui != null) { - ui.write(getWixType(), this, xml); + if (uiSpec.isPresent()) { + writeNonEmptyUIElement(xml); } else { xml.writeStartElement("UI"); xml.writeAttribute("Id", "JpUI"); @@ -159,371 +184,140 @@ final class WixUiFragmentBuilder extends WixFragmentBuilder { } } - private UI getUI() { - if (withInstallDirChooserDlg || withShortcutPromptDlg) { - // WixUI_InstallDir for shortcut prompt dialog too because in - // WixUI_Minimal UI sequence WelcomeEulaDlg dialog doesn't have "Next" - // button, but has "Install" button. So inserting shortcut prompt dialog - // after welcome dialog in WixUI_Minimal UI sequence would be confusing - return UI.InstallDir; - } else if (withLicenseDlg) { - return UI.Minimal; - } else { - return null; - } - } + void writeNonEmptyUIElement(XMLStreamWriter xml) throws XMLStreamException, IOException { - private enum UI { - InstallDir("WixUI_InstallDir", - WixUiFragmentBuilder::dialogSequenceForWixUI_InstallDir, - Dialog::createPairsForWixUI_InstallDir), - Minimal("WixUI_Minimal", null, null); + switch (getWixType()) { + case Wix3 -> {} + case Wix4 -> { + // https://wixtoolset.org/docs/fourthree/faqs/#converting-custom-wixui-dialog-sets + xml.writeProcessingInstruction("foreach WIXUIARCH in X86;X64;A64"); + writeWix4UIRef(xml, uiSpec.get().wixUI().id(), "JpUIInternal_$(WIXUIARCH)"); + xml.writeProcessingInstruction("endforeach"); - UI(String wixUIRef, - Function> dialogIdsSupplier, - Supplier>> dialogPairsSupplier) { - this.wixUIRef = wixUIRef; - this.dialogIdsSupplier = dialogIdsSupplier; - this.dialogPairsSupplier = dialogPairsSupplier; - } - - void write(WixToolsetType wixType, WixUiFragmentBuilder outer, XMLStreamWriter xml) throws XMLStreamException, IOException { - switch (wixType) { - case Wix3 -> {} - case Wix4 -> { - // https://wixtoolset.org/docs/fourthree/faqs/#converting-custom-wixui-dialog-sets - xml.writeProcessingInstruction("foreach WIXUIARCH in X86;X64;A64"); - writeWix4UIRef(xml, wixUIRef, "JpUIInternal_$(WIXUIARCH)"); - xml.writeProcessingInstruction("endforeach"); - - writeWix4UIRef(xml, "JpUIInternal", "JpUI"); - } - default -> { - throw new IllegalArgumentException(); - } - } - - xml.writeStartElement("UI"); - switch (wixType) { - case Wix3 -> { - xml.writeAttribute("Id", "JpUI"); - xml.writeStartElement("UIRef"); - xml.writeAttribute("Id", wixUIRef); - xml.writeEndElement(); // UIRef - } - case Wix4 -> { - xml.writeAttribute("Id", "JpUIInternal"); - } - default -> { - throw new IllegalArgumentException(); - } - } - writeContents(wixType, outer, xml); - xml.writeEndElement(); // UI - } - - private void writeContents(WixToolsetType wixType, WixUiFragmentBuilder outer, - XMLStreamWriter xml) throws XMLStreamException, IOException { - if (dialogIdsSupplier != null) { - List dialogIds = dialogIdsSupplier.apply(outer); - Map> dialogPairs = dialogPairsSupplier.get(); - - if (dialogIds.contains(Dialog.InstallDirDlg)) { - xml.writeStartElement("DialogRef"); - xml.writeAttribute("Id", "InstallDirNotEmptyDlg"); - xml.writeEndElement(); // DialogRef - } - - var it = dialogIds.iterator(); - Dialog firstId = it.next(); - while (it.hasNext()) { - Dialog secondId = it.next(); - DialogPair pair = new DialogPair(firstId, secondId); - for (var curPair : List.of(pair, pair.flip())) { - for (var publish : dialogPairs.get(curPair)) { - writePublishDialogPair(wixType, xml, publish, curPair); - } - } - firstId = secondId; - } + writeWix4UIRef(xml, "JpUIInternal", "JpUI"); } } - private static void writeWix4UIRef(XMLStreamWriter xml, String uiRef, String id) throws XMLStreamException, IOException { - // https://wixtoolset.org/docs/fourthree/faqs/#referencing-the-standard-wixui-dialog-sets - xml.writeStartElement("UI"); - xml.writeAttribute("Id", id); - xml.writeStartElement("ui:WixUI"); - xml.writeAttribute("Id", uiRef); - xml.writeNamespace("ui", "http://wixtoolset.org/schemas/v4/wxs/ui"); - xml.writeEndElement(); // UIRef - xml.writeEndElement(); // UI - } - - private final String wixUIRef; - private final Function> dialogIdsSupplier; - private final Supplier>> dialogPairsSupplier; - } - - private List dialogSequenceForWixUI_InstallDir() { - List dialogIds = new ArrayList<>( - List.of(Dialog.WixUI_WelcomeDlg)); - if (withLicenseDlg) { - dialogIds.add(Dialog.WixUI_LicenseAgreementDlg); - } - - if (withInstallDirChooserDlg) { - dialogIds.add(Dialog.InstallDirDlg); - } - - if (withShortcutPromptDlg) { - dialogIds.add(Dialog.ShortcutPromptDlg); - } - - dialogIds.add(Dialog.WixUI_VerifyReadyDlg); - - return dialogIds; - } - - private enum Dialog { - WixUI_WelcomeDlg, - WixUI_LicenseAgreementDlg, - InstallDirDlg, - ShortcutPromptDlg, - WixUI_VerifyReadyDlg; - - Dialog() { - if (name().startsWith("WixUI_")) { - id = name().substring("WixUI_".length()); - } else { - id = name(); + xml.writeStartElement("UI"); + switch (getWixType()) { + case Wix3 -> { + xml.writeAttribute("Id", "JpUI"); + xml.writeStartElement("UIRef"); + xml.writeAttribute("Id", uiSpec.get().wixUI().id()); + xml.writeEndElement(); // UIRef + } + case Wix4 -> { + xml.writeAttribute("Id", "JpUIInternal"); } } - - static Map> createPair(Dialog firstId, - Dialog secondId, List nextBuilders, - List prevBuilders) { - var pair = new DialogPair(firstId, secondId); - return Map.of(pair, nextBuilders.stream().map(b -> { - return buildPublish(b.create()).next().create(); - }).toList(), pair.flip(), - prevBuilders.stream().map(b -> { - return buildPublish(b.create()).back().create(); - }).toList()); - } - - static Map> createPair(Dialog firstId, - Dialog secondId, List builders) { - return createPair(firstId, secondId, builders, builders); - } - - static Map> createPairsForWixUI_InstallDir() { - Map> map = new HashMap<>(); - - // Order is a "weight" of action. If there are multiple - // "NewDialog" action for the same dialog Id, MSI would pick the one - // with higher order value. In WixUI_InstallDir dialog sequence the - // highest order value is 4. InstallDirNotEmptyDlg adds NewDialog - // action with order 5. Setting order to 6 for all - // actions configured in this function would make them executed - // instead of corresponding default actions defined in - // WixUI_InstallDir dialog sequence. - var order = 6; - - // Based on WixUI_InstallDir.wxs - var backFromVerifyReadyDlg = List.of(buildPublish().condition( - "NOT Installed").order(order)); - var uncondinal = List.of(buildPublish().condition("1")); - var ifNotIstalled = List.of( - buildPublish().condition("NOT Installed").order(order)); - var ifLicenseAccepted = List.of(buildPublish().condition( - "LicenseAccepted = \"1\"").order(order)); - - // Empty condition list for the default dialog sequence - map.putAll(createPair(WixUI_WelcomeDlg, WixUI_LicenseAgreementDlg, - List.of())); - map.putAll( - createPair(WixUI_WelcomeDlg, InstallDirDlg, ifNotIstalled)); - map.putAll(createPair(WixUI_WelcomeDlg, ShortcutPromptDlg, - ifNotIstalled)); - - map.putAll(createPair(WixUI_LicenseAgreementDlg, InstallDirDlg, - List.of())); - map.putAll(createPair(WixUI_LicenseAgreementDlg, ShortcutPromptDlg, - ifLicenseAccepted, uncondinal)); - map.putAll(createPair(WixUI_LicenseAgreementDlg, - WixUI_VerifyReadyDlg, ifLicenseAccepted, - backFromVerifyReadyDlg)); - - map.putAll(createPair(InstallDirDlg, ShortcutPromptDlg, List.of(), - uncondinal)); - map.putAll(createPair(InstallDirDlg, WixUI_VerifyReadyDlg, List.of())); - - map.putAll(createPair(ShortcutPromptDlg, WixUI_VerifyReadyDlg, - uncondinal, backFromVerifyReadyDlg)); - - return map; - } - - private final String id; + writeUIElementContents(xml); + xml.writeEndElement(); // UI } - private static final class DialogPair { + private void writeUIElementContents(XMLStreamWriter xml) throws XMLStreamException, IOException { - DialogPair(Dialog first, Dialog second) { - this(first.id, second.id); + if (uiConfig.isWithInstallDirChooserDlg()) { + xml.writeStartElement("DialogRef"); + xml.writeAttribute("Id", "InstallDirNotEmptyDlg"); + xml.writeEndElement(); // DialogRef } - DialogPair(String firstId, String secondId) { - this.firstId = firstId; - this.secondId = secondId; + for (var e : uiSpec.get().customDialogSequence().entrySet().stream() + .sorted(Comparator.comparing(Map.Entry::getKey, DialogPair.DEFAULT_COMPARATOR)) + .toList()) { + writePublishDialogPair(getWixType(), xml, e.getValue(), e.getKey()); } - DialogPair flip() { - return new DialogPair(secondId, firstId); - } - - @Override - public int hashCode() { - int hash = 3; - hash = 97 * hash + Objects.hashCode(this.firstId); - hash = 97 * hash + Objects.hashCode(this.secondId); - return hash; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final DialogPair other = (DialogPair) obj; - if (!Objects.equals(this.firstId, other.firstId)) { - return false; - } - if (!Objects.equals(this.secondId, other.secondId)) { - return false; - } - return true; - } - - private final String firstId; - private final String secondId; + hideDialogs(getWixType(), xml, uiSpec.get().hideDialogs()); } - private static final class Publish { - - Publish(String control, String condition, int order) { - this.control = control; - this.condition = condition; - this.order = order; - } - - private final String control; - private final String condition; - private final int order; + private static void writeWix4UIRef(XMLStreamWriter xml, String uiRef, String id) throws XMLStreamException, IOException { + // https://wixtoolset.org/docs/fourthree/faqs/#referencing-the-standard-wixui-dialog-sets + xml.writeStartElement("UI"); + xml.writeAttribute("Id", Objects.requireNonNull(id)); + xml.writeStartElement("ui:WixUI"); + xml.writeAttribute("Id", Objects.requireNonNull(uiRef)); + xml.writeNamespace("ui", "http://wixtoolset.org/schemas/v4/wxs/ui"); + xml.writeEndElement(); // UIRef + xml.writeEndElement(); // UI } - private static final class PublishBuilder { + private static void writePublishDialogPair( + WixToolsetType wixType, + XMLStreamWriter xml, + Publish publish, + DialogPair dialogPair) throws IOException, XMLStreamException { - PublishBuilder() { - order(0); - next(); - condition("1"); - } - - PublishBuilder(Publish publish) { - order(publish.order); - control(publish.control); - condition(publish.condition); - } - - public PublishBuilder control(String v) { - control = v; - return this; - } - - public PublishBuilder next() { - return control("Next"); - } - - public PublishBuilder back() { - return control("Back"); - } - - public PublishBuilder condition(String v) { - condition = v; - return this; - } - - public PublishBuilder order(int v) { - order = v; - return this; - } - - Publish create() { - return new Publish(control, condition, order); - } - - private String control; - private String condition; - private int order; - } - - private static PublishBuilder buildPublish() { - return new PublishBuilder(); - } - - private static PublishBuilder buildPublish(Publish publish) { - return new PublishBuilder(publish); - } - - private static void writePublishDialogPair(WixToolsetType wixType, XMLStreamWriter xml, - Publish publish, DialogPair dialogPair) throws IOException, XMLStreamException { xml.writeStartElement("Publish"); - xml.writeAttribute("Dialog", dialogPair.firstId); - xml.writeAttribute("Control", publish.control); + xml.writeAttribute("Dialog", dialogPair.first().id()); + xml.writeAttribute("Control", publish.control().id()); xml.writeAttribute("Event", "NewDialog"); - xml.writeAttribute("Value", dialogPair.secondId); - if (publish.order != 0) { - xml.writeAttribute("Order", String.valueOf(publish.order)); + xml.writeAttribute("Value", dialogPair.second().id()); + if (publish.order() != 0) { + xml.writeAttribute("Order", String.valueOf(publish.order())); } + switch (wixType) { - case Wix3 -> xml.writeCharacters(publish.condition); - case Wix4 -> xml.writeAttribute("Condition", publish.condition); - default -> throw new IllegalArgumentException(); + case Wix3 -> { + xml.writeCharacters(publish.condition()); + } + case Wix4 -> { + xml.writeAttribute("Condition", publish.condition()); + } } + + xml.writeEndElement(); + } + + private static void hideDialogs( + WixToolsetType wixType, + XMLStreamWriter xml, + Collection hideDialogs) throws IOException, XMLStreamException { + + if (!hideDialogs.isEmpty()) { + if (wixType == WixToolsetType.Wix4) { + xml.writeStartElement("InstallUISequence"); + for (var showAction : hideDialogs.stream().sorted(ShowActionSuppresser.DEFAULT_COMPARATOR).toList()) { + writeWix4ShowAction(wixType, xml, showAction); + } + xml.writeEndElement(); + } + } + } + + private static void writeWix4ShowAction( + WixToolsetType wixType, + XMLStreamWriter xml, + ShowActionSuppresser hideDialog) throws IOException, XMLStreamException { + + xml.writeStartElement("Show"); + xml.writeAttribute("Dialog", String.format("override %s", hideDialog.dialog().id())); + xml.writeAttribute(switch (hideDialog.order()) { + case AFTER -> "After"; + }, hideDialog.anchor().id()); + xml.writeAttribute("Condition", "0"); xml.writeEndElement(); } private final class CustomDialog { - CustomDialog(Function createResource, String category, - String wxsFileName) { + CustomDialog(Function createResource, String category, String wxsFileName) { this.wxsFileName = wxsFileName; this.wixVariables = new WixVariables(); - addResource(createResource.apply(wxsFileName).setCategory(category).setPublicName( - wxsFileName), wxsFileName); + addResource(createResource.apply(wxsFileName).setCategory(category).setPublicName(wxsFileName), wxsFileName); } void addToWixPipeline(WixPipeline.Builder wixPipeline) { - wixPipeline.addSource(getConfigRoot().toAbsolutePath().resolve( - wxsFileName), wixVariables.getValues()); + wixPipeline.addSource(getConfigRoot().toAbsolutePath().resolve(wxsFileName), wixVariables); } private final WixVariables wixVariables; private final String wxsFileName; } - private boolean withInstallDirChooserDlg; - private boolean withShortcutPromptDlg; - private boolean withLicenseDlg; + private UIConfig uiConfig; + private Optional uiSpec; private boolean withCustomActionsDll = true; private List customDialogs; } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixVariables.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixVariables.java index 36ed1d99738..88a25810182 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixVariables.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixVariables.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -24,22 +24,103 @@ */ package jdk.jpackage.internal; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; +import jdk.jpackage.internal.WixToolset.WixToolsetType; final class WixVariables { - void defineWixVariable(String variableName) { - setWixVariable(variableName, "yes"); + WixVariables() { + this.values = new HashMap<>(); } - void setWixVariable(String variableName, String variableValue) { - values.put(variableName, variableValue); + private WixVariables(Map values) { + this.values = values; + this.isImmutable = true; } - Map getValues() { - return values; + WixVariables define(String variableName) { + return put(variableName, "yes"); } - private final Map values = new HashMap<>(); + WixVariables put(String variableName, String variableValue) { + Objects.requireNonNull(variableName); + Objects.requireNonNull(variableValue); + validateMutable(); + values.compute(variableName, (k, v) -> { + if (!allowOverrides && v != null) { + throw overridingDisabled(); + } + return variableValue; + }); + return this; + } + + WixVariables putAll(Map values) { + Objects.requireNonNull(values); + validateMutable(); + if (!allowOverrides && !Collections.disjoint(this.values.keySet(), values.keySet())) { + throw overridingDisabled(); + } else { + values.entrySet().forEach(e -> { + put(e.getKey(), e.getValue()); + }); + } + return this; + } + + WixVariables putAll(WixVariables other) { + return putAll(other.values); + } + + WixVariables allowOverrides(boolean v) { + validateMutable(); + allowOverrides = v; + return this; + } + + WixVariables createdImmutableCopy() { + if (isImmutable) { + return this; + } else { + return new WixVariables(Map.copyOf(values)); + } + } + + List toWixCommandLine(WixToolsetType wixType) { + var orderedWixVars = values.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)); + return (switch (Objects.requireNonNull(wixType)) { + case Wix3 -> { + yield orderedWixVars.map(wixVar -> { + return String.format("-d%s=%s", wixVar.getKey(), wixVar.getValue()); + }); + } + case Wix4 -> { + yield orderedWixVars.flatMap(wixVar -> { + return Stream.of("-d", String.format("%s=%s", wixVar.getKey(), wixVar.getValue())); + }); + } + }).toList(); + } + + private void validateMutable() { + if (isImmutable) { + throw new IllegalStateException("WiX variables container is immutable"); + } + } + + private static IllegalStateException overridingDisabled() { + return new IllegalStateException("Overriding variables is unsupported"); + } + + private final Map values; + private boolean allowOverrides; + private boolean isImmutable; + + static final WixVariables EMPTY = new WixVariables().createdImmutableCopy(); } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinMsiPackageMixin.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinMsiPackageMixin.java index 94d7c30fe57..ec004da2cee 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinMsiPackageMixin.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinMsiPackageMixin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -36,6 +36,8 @@ public interface WinMsiPackageMixin { boolean withShortcutPrompt(); + boolean withUI(); + Optional helpURL(); Optional updateURL(); @@ -50,8 +52,16 @@ public interface WinMsiPackageMixin { Optional serviceInstaller(); - record Stub(DottedVersion msiVersion, boolean withInstallDirChooser, boolean withShortcutPrompt, - Optional helpURL, Optional updateURL, String startMenuGroupName, - boolean isSystemWideInstall, UUID upgradeCode, UUID productCode, + record Stub( + DottedVersion msiVersion, + boolean withInstallDirChooser, + boolean withShortcutPrompt, + boolean withUI, + Optional helpURL, + Optional updateURL, + String startMenuGroupName, + boolean isSystemWideInstall, + UUID upgradeCode, + UUID productCode, Optional serviceInstaller) implements WinMsiPackageMixin {} } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/msi-disable-actions.js b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/msi-disable-actions.js new file mode 100644 index 00000000000..2f0f3a5d019 --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/msi-disable-actions.js @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026, 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. + */ + + +function modifyMsi(msiPath, callback) { + var installer = new ActiveXObject('WindowsInstaller.Installer') + var database = installer.OpenDatabase(msiPath, 1 /* msiOpenDatabaseModeTransact */) + + callback(installer, database) + + database.Commit() +} + + +function disableActions(installer, db, sequence, actionIDs) { + var tables = {} + + var view = db.OpenView("SELECT `Action`, `Condition`, `Sequence` FROM " + sequence) + view.Execute() + + try { + while (true) { + var record = view.Fetch() + if (!record) { + break + } + + var action = record.StringData(1) + + if (actionIDs.hasOwnProperty(action)) { + WScript.Echo("Set condition of [" + action + "] action in [" + sequence + "] sequence to [0]") + var newRecord = installer.CreateRecord(3) + for (var i = 1; i !== newRecord.FieldCount + 1; i++) { + newRecord.StringData(i) = record.StringData(i) + } + newRecord.StringData(2) = "0" // Set condition value to `0` + view.Modify(3 /* msiViewModifyAssign */, newRecord) // Replace existing record + } + } + } finally { + view.Close() + } +} + + +(function () { + var msi = WScript.arguments(0) + var sequence = WScript.arguments(1) + var actionIDs = {} + for (var i = 0; i !== WScript.arguments.Count(); i++) { + actionIDs[WScript.arguments(i)] = true + } + + modifyMsi(msi, function (installer, db) { + disableActions(installer, db, sequence, actionIDs) + }) +})() diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Control.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Control.java new file mode 100644 index 00000000000..d41fc435126 --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Control.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2026, 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. + */ +package jdk.jpackage.internal.wixui; + +import java.util.Comparator; + +/** + * WiX Dialog's control. + */ +public sealed interface Control permits StandardControl { + String id(); + + public static final Comparator DEFAULT_COMPARATOR = Comparator.comparing(Control::id); +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/CustomDialog.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/CustomDialog.java new file mode 100644 index 00000000000..7325685469c --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/CustomDialog.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2026, 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. + */ +package jdk.jpackage.internal.wixui; + +/** + * Custom jpackage dialogs. + */ +public enum CustomDialog implements Dialog { + ShortcutPromptDlg, + ; + + @Override + public String id() { + return name(); + } +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Dialog.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Dialog.java new file mode 100644 index 00000000000..a43648042a9 --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Dialog.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2026, 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. + */ +package jdk.jpackage.internal.wixui; + +import java.util.Comparator; + +/** + * WiX Dialog. + */ +public sealed interface Dialog permits WixDialog, CustomDialog { + String id(); + + public static final Comparator DEFAULT_COMPARATOR = Comparator.comparing(Dialog::id); +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/DialogPair.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/DialogPair.java new file mode 100644 index 00000000000..7f0f3bb6090 --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/DialogPair.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2026, 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. + */ +package jdk.jpackage.internal.wixui; + +import static java.util.Comparator.comparing; + +import java.util.Comparator; +import java.util.Objects; + +public record DialogPair(Dialog first, Dialog second) { + + public DialogPair { + Objects.requireNonNull(first); + Objects.requireNonNull(second); + if (first.equals(second) || first.id().equals(second.id())) { + throw new IllegalArgumentException("Dialogs must be different"); + } + } + + DialogPair flip() { + return new DialogPair(second, first); + } + + public static final Comparator DEFAULT_COMPARATOR = + comparing(DialogPair::first, Dialog.DEFAULT_COMPARATOR) + .thenComparing(comparing(DialogPair::second, Dialog.DEFAULT_COMPARATOR)); +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Publish.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Publish.java new file mode 100644 index 00000000000..7741ad823bb --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Publish.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2026, 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. + */ +package jdk.jpackage.internal.wixui; + +import java.util.Objects; + +public record Publish(Control control, String condition, int order) { + + public Publish { + Objects.requireNonNull(control); + Objects.requireNonNull(condition); + if (order < 0) { + throw new IllegalArgumentException("Negative order"); + } + } + + Builder toBuilder() { + return new Builder(this); + } + + static Builder build() { + return new Builder(); + } + + static final class Builder { + + private Builder() { + order(0); + next(); + condition("1"); + } + + private Builder(Publish publish) { + order(publish.order); + control(publish.control); + condition(publish.condition); + } + + Publish create() { + return new Publish(control, condition, order); + } + + Builder control(Control v) { + control = v; + return this; + } + + Builder next() { + return control(StandardControl.NEXT); + } + + Builder back() { + return control(StandardControl.BACK); + } + + Builder condition(String v) { + condition = v; + return this; + } + + Builder order(int v) { + order = v; + return this; + } + + private int order; + private Control control; + private String condition; + } +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/ShowActionSuppresser.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/ShowActionSuppresser.java new file mode 100644 index 00000000000..f89b15987ee --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/ShowActionSuppresser.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2026, 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. + */ +package jdk.jpackage.internal.wixui; + +import static java.util.Comparator.comparing; + +import java.util.Comparator; +import java.util.Objects; + +public record ShowActionSuppresser(Dialog dialog, Dialog anchor, Order order) { + + public enum Order { + AFTER, + ; + } + + public ShowActionSuppresser { + Objects.requireNonNull(order); + validate(dialog); + validate(anchor); + } + + static Builder suppressShowAction(WixDialog dialog) { + return new Builder(dialog); + } + + static final class Builder { + + private Builder(WixDialog dialog) { + this.dialog = Objects.requireNonNull(dialog); + } + + ShowActionSuppresser after(WixDialog anchor) { + return new ShowActionSuppresser(dialog, anchor, Order.AFTER); + } + + private final WixDialog dialog; + } + + private static void validate(Dialog v) { + if (!(Objects.requireNonNull(v) instanceof WixDialog)) { + throw new IllegalArgumentException(); + } + } + + public static final Comparator DEFAULT_COMPARATOR = + comparing(ShowActionSuppresser::dialog, Dialog.DEFAULT_COMPARATOR) + .thenComparing(comparing(ShowActionSuppresser::anchor, Dialog.DEFAULT_COMPARATOR)) + .thenComparing(comparing(ShowActionSuppresser::order)); +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/StandardControl.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/StandardControl.java new file mode 100644 index 00000000000..43c5c5a7e9e --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/StandardControl.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2026, 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. + */ +package jdk.jpackage.internal.wixui; + +import java.util.Objects; + +/** + * Dialog controls referenced in adjustments of the standard WiX UI. + */ +enum StandardControl implements Control { + NEXT("Next"), + BACK("Back"), + ; + + StandardControl(String id) { + this.id = Objects.requireNonNull(id); + } + + @Override + public String id() { + return id; + } + + private final String id; +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/UIConfig.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/UIConfig.java new file mode 100644 index 00000000000..c695694788f --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/UIConfig.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026, 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. + */ +package jdk.jpackage.internal.wixui; + +/** + * UI config. + */ +public record UIConfig( + boolean isWithInstallDirChooserDlg, + boolean isWithShortcutPromptDlg, + boolean isWithLicenseDlg) { + + public static Builder build() { + return new Builder(); + } + + public static final class Builder { + + private Builder() { + } + + public UIConfig create() { + return new UIConfig(isWithInstallDirChooserDlg, isWithShortcutPromptDlg, isWithLicenseDlg); + } + + public Builder withInstallDirChooserDlg(boolean v) { + isWithInstallDirChooserDlg = v; + return this; + } + + public Builder withInstallDirChooserDlg() { + return withInstallDirChooserDlg(true); + } + + public Builder withShortcutPromptDlg(boolean v) { + isWithShortcutPromptDlg = v; + return this; + } + + public Builder withShortcutPromptDlg() { + return withShortcutPromptDlg(true); + } + + public Builder withLicenseDlg(boolean v) { + isWithLicenseDlg = v; + return this; + } + + public Builder withLicenseDlg() { + return withLicenseDlg(true); + } + + private boolean isWithInstallDirChooserDlg; + private boolean isWithShortcutPromptDlg; + private boolean isWithLicenseDlg; + } +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/UISpec.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/UISpec.java new file mode 100644 index 00000000000..38cf3775c2b --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/UISpec.java @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2026, 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. + */ +package jdk.jpackage.internal.wixui; + +import static jdk.jpackage.internal.wixui.CustomDialog.ShortcutPromptDlg; +import static jdk.jpackage.internal.wixui.ShowActionSuppresser.suppressShowAction; +import static jdk.jpackage.internal.wixui.WixDialog.InstallDirDlg; +import static jdk.jpackage.internal.wixui.WixDialog.LicenseAgreementDlg; +import static jdk.jpackage.internal.wixui.WixDialog.ProgressDlg; +import static jdk.jpackage.internal.wixui.WixDialog.VerifyReadyDlg; +import static jdk.jpackage.internal.wixui.WixDialog.WelcomeDlg; +import static jdk.jpackage.internal.wixui.WixDialog.WelcomeEulaDlg; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +/** + * UI spec. + *

+ * UI is based on one of the standard WiX UIs with optional alterations. + */ +public record UISpec( + WixUI wixUI, + Map wixVariables, + Map customDialogSequence, + Collection hideDialogs) { + + public UISpec { + Objects.requireNonNull(wixUI); + Objects.requireNonNull(wixVariables); + Objects.requireNonNull(customDialogSequence); + Objects.requireNonNull(hideDialogs); + } + + static Builder build(WixUI wixUI) { + return new Builder().wixUI(wixUI); + } + + static final class Builder { + + private Builder() { + } + + UISpec create() { + return new UISpec( + wixUI, + Optional.ofNullable(wixVariables).map(Collections::unmodifiableMap).orElseGet(Map::of), + Optional.ofNullable(customDialogSequence).map(Collections::unmodifiableMap).orElseGet(Map::of), + Optional.ofNullable(hideDialogs).map(List::copyOf).orElseGet(List::of)); + } + + Builder wixUI(WixUI v) { + wixUI = v; + return this; + } + + Builder setWixVariable(String name, String value) { + wixVariables.put(Objects.requireNonNull(name), Objects.requireNonNull(value)); + return this; + } + + Builder customDialogSequence(Map v) { + customDialogSequence = v; + return this; + } + + Builder hideDialogs(Collection v) { + hideDialogs = v; + return this; + } + + Builder hideDialogs(ShowActionSuppresser... v) { + return hideDialogs(List.of(v)); + } + + private WixUI wixUI; + private final Map wixVariables = new HashMap<>(); + private Map customDialogSequence; + private Collection hideDialogs; + } + + public static UISpec create(UIConfig cfg) { + Objects.requireNonNull(cfg); + return Optional.ofNullable(DEFAULT_SPECS.get(cfg)).map(Supplier::get).orElseGet(() -> { + return createCustom(cfg); + }); + } + + private static UISpec createCustom(UIConfig cfg) { + Objects.requireNonNull(cfg); + + var dialogs = installDirUiDialogs(cfg); + var dialogPairs = toDialogPairs(dialogs); + + var customDialogSequence = overrideInstallDirDialogSequence().stream().filter(e -> { + return dialogPairs.contains(e.getKey()); + }).collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue)); + + var uiSpec = build(WixUI.INSTALL_DIR).customDialogSequence(customDialogSequence); + + var it = dialogs.iterator(); + do { + if (it.next().equals(InstallDirDlg)) { + uiSpec.setWixVariable("JpAfterInstallDirDlg", it.next().id()); + } + } while (it.hasNext()); + + return uiSpec.create(); + } + + private static void createPairs( + BiConsumer sink, + Dialog first, + Dialog second, + Publish publishNext, + Publish publishPrev) { + + createPairNext(sink, first, second, publishNext); + createPairBack(sink, second, first, publishPrev); + } + + private static void createPairs( + BiConsumer sink, + Dialog first, + Dialog second, + Publish publish) { + createPairs(sink, first, second, publish, publish); + } + + private static void createPairNext( + BiConsumer sink, + Dialog first, + Dialog second, + Publish publish) { + + var pair = new DialogPair(first, second); + + sink.accept(pair, publish.toBuilder().next().create()); + } + + private static void createPairBack( + BiConsumer sink, + Dialog first, + Dialog second, + Publish publish) { + + var pair = new DialogPair(first, second); + + sink.accept(pair, publish.toBuilder().back().create()); + } + + private static Collection toDialogPairs(List

dialogs) { + if (dialogs.size() < 2) { + throw new IllegalArgumentException(); + } + + var pairs = new ArrayList(); + var it = dialogs.listIterator(); + var prev = it.next(); + do { + var next = it.next(); + var pair = new DialogPair(prev, next); + pairs.add(pair); + pairs.add(pair.flip()); + prev = next; + } while (it.hasNext()); + + return pairs; + } + + private static List installDirUiDialogs(UIConfig cfg) { + var dialogs = new ArrayList(); + + dialogs.add(WelcomeDlg); + + if (cfg.isWithLicenseDlg()) { + dialogs.add(LicenseAgreementDlg); + } + + if (cfg.isWithInstallDirChooserDlg()) { + dialogs.add(InstallDirDlg); + } + + if (cfg.isWithShortcutPromptDlg()) { + dialogs.add(ShortcutPromptDlg); + } + + dialogs.add(VerifyReadyDlg); + + return dialogs; + } + + private static Collection> overrideInstallDirDialogSequence() { + + List> entries = new ArrayList<>(); + + BiConsumer acc = (pair, publish) -> { + entries.add(Map.entry(pair, publish)); + }; + + // Order is a "weight" of action. If there are multiple + // "NewDialog" action for the same dialog Id, MSI would pick the one + // with higher order value. In WixUI_InstallDir dialog sequence the + // highest order value is 4. InstallDirNotEmptyDlg adds NewDialog + // action with order 5. Setting order to 6 for all + // actions configured in this function would make them executed + // instead of corresponding default actions defined in + // WixUI_InstallDir dialog sequence. + var order = 6; + + // Based on WixUI_InstallDir.wxs + var backFromVerifyReadyDlg = Publish.build().condition(CONDITION_NOT_INSTALLED).order(order).create(); + var uncondinal = Publish.build().condition(CONDITION_ALWAYS).create(); + var ifNotIstalled = Publish.build().condition(CONDITION_NOT_INSTALLED).order(order).create(); + var ifLicenseAccepted = Publish.build().condition("LicenseAccepted = \"1\"").order(order).create(); + + // Define all alternative transitions: + // - Skip standard license dialog + // - Insert shortcut prompt dialog after the standard install dir dialog + // - Replace the standard install dir dialog with the shortcut prompt dialog + + createPairs(acc, WelcomeDlg, InstallDirDlg, ifNotIstalled); + createPairs(acc, WelcomeDlg, VerifyReadyDlg, ifNotIstalled); + createPairs(acc, WelcomeDlg, ShortcutPromptDlg, ifNotIstalled); + + createPairs(acc, LicenseAgreementDlg, ShortcutPromptDlg, ifLicenseAccepted, uncondinal); + createPairs(acc, LicenseAgreementDlg, VerifyReadyDlg, ifLicenseAccepted, backFromVerifyReadyDlg); + + createPairs(acc, InstallDirDlg, ShortcutPromptDlg, uncondinal); + + createPairs(acc, ShortcutPromptDlg, VerifyReadyDlg, uncondinal, backFromVerifyReadyDlg); + + return entries; + } + + private static final Map> DEFAULT_SPECS; + + private static final String CONDITION_ALWAYS = "1"; + private static final String CONDITION_NOT_INSTALLED = "NOT Installed"; + + static { + + var specs = new HashMap>(); + + // Verbatim WiX "Minimal" dialog set. + specs.put(UIConfig.build() + .withLicenseDlg() + .create(), () -> { + return build(WixUI.MINIMAL).create(); + }); + + // Standard WiX "Minimal" dialog set without the license dialog. + // The license dialog is removed by overriding the default "Show" + // action with the condition that always evaluates to "FALSE". + specs.put(UIConfig.build() + .create(), () -> { + return build(WixUI.MINIMAL).hideDialogs(suppressShowAction(WelcomeEulaDlg).after(ProgressDlg)).create(); + }); + + DEFAULT_SPECS = Collections.unmodifiableMap(specs); + } +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/WixDialog.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/WixDialog.java new file mode 100644 index 00000000000..95c415dcaa0 --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/WixDialog.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026, 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. + */ +package jdk.jpackage.internal.wixui; + +/** + * Standard WiX dialogs. + */ +enum WixDialog implements Dialog { + InstallDirDlg, + LicenseAgreementDlg, + ProgressDlg, + VerifyReadyDlg, + WelcomeDlg, + WelcomeEulaDlg, + ; + + @Override + public String id() { + return name(); + } +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/WixUI.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/WixUI.java new file mode 100644 index 00000000000..1b87a7fee2d --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/WixUI.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2026, 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. + */ +package jdk.jpackage.internal.wixui; + +import java.util.Objects; + +/** + * Standard WiX UI. + */ +public enum WixUI { + MINIMAL("WixUI_Minimal"), + INSTALL_DIR("WixUI_InstallDir"), + ; + + WixUI(String id) { + this.id = Objects.requireNonNull(id); + } + + public String id() { + return id; + } + + private final String id; +} diff --git a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java index 57d427c1774..eba81b4be07 100644 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -49,6 +49,7 @@ import java.util.List; import java.util.ListIterator; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutorService; @@ -181,6 +182,7 @@ class ConsoleIOContext extends IOContext { setupReader.accept(reader); reader.setOpt(Option.DISABLE_EVENT_EXPANSION); + reader.setVariable(LineReader.WORDCHARS, "_$"); reader.setParser((line, cursor, context) -> { if (!ConsoleIOContext.this.allowIncompleteInputs && !repl.isComplete(line)) { @@ -201,6 +203,7 @@ class ConsoleIOContext extends IOContext { .filter(key -> key.startsWith(HISTORY_LINE_PREFIX)) .sorted() .map(key -> repl.prefs.get(key)) + .filter(Objects::nonNull) .forEach(loadHistory::add); for (ListIterator it = loadHistory.listIterator(); it.hasNext(); ) { diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahGlobalHeuristic.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahGlobalHeuristic.cpp new file mode 100644 index 00000000000..7b3e4227b5c --- /dev/null +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahGlobalHeuristic.cpp @@ -0,0 +1,332 @@ +/* + * Copyright Amazon.com Inc. 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. + */ + +#include "unittest.hpp" +#include "gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp" + +// Region size of 256K is the minimum Shenandoah region size. +static const size_t REGION_SIZE = 256 * 1024; + +// Waste factors matching the default JVM flag values. +static const double OLD_EVAC_WASTE = ShenandoahOldEvacWaste; +static const double PROMO_EVAC_WASTE = ShenandoahPromoEvacWaste; +static const double YOUNG_EVAC_WASTE = ShenandoahEvacWaste; + +// Default thresholds as percentages of region size. +static const size_t GARBAGE_THRESHOLD = REGION_SIZE * 25 / 100; +static const size_t IGNORE_THRESHOLD = REGION_SIZE * 5 / 100; + +static ShenandoahGlobalCSetBudget make_budget(size_t shared_reserves, + size_t min_garbage = 0, + size_t young_evac_reserve = 0, + size_t old_evac_reserve = 0, + size_t promo_reserve = 0) { + return ShenandoahGlobalCSetBudget(REGION_SIZE, shared_reserves, + GARBAGE_THRESHOLD, IGNORE_THRESHOLD, min_garbage, + young_evac_reserve, YOUNG_EVAC_WASTE, + old_evac_reserve, OLD_EVAC_WASTE, + promo_reserve, PROMO_EVAC_WASTE); +} + +// ---- Threshold tests ---- + +// A region whose garbage is 1 byte below the 25% garbage threshold should be +// skipped, even when there is plenty of shared reserve available. +TEST(ShenandoahGlobalCSet, skip_below_garbage_threshold) { + auto budget = make_budget(10 * REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD - 1, REGION_SIZE - (GARBAGE_THRESHOLD - 1), 0, false, false + }; + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::SKIP); +} + +// When the cumulative garbage collected so far is below min_garbage, regions +// with garbage above the ignore threshold (5%) but below the garbage threshold +// (25%) are added anyway ("add_regardless" path). This young non-tenurable +// region should be accepted as a young evacuation. +TEST(ShenandoahGlobalCSet, add_regardless_when_below_min_garbage) { + auto budget = make_budget(10 * REGION_SIZE, /*min_garbage=*/REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + IGNORE_THRESHOLD + 1, 1000, 0, false, false + }; + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::ADD_YOUNG_EVAC); +} + +// A region whose garbage is exactly at the ignore threshold (not above it) +// should be skipped even when min_garbage has not been met, because the +// add_regardless condition requires garbage strictly above the ignore threshold. +TEST(ShenandoahGlobalCSet, skip_at_ignore_threshold) { + auto budget = make_budget(10 * REGION_SIZE, /*min_garbage=*/REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + IGNORE_THRESHOLD, 1000, 0, false, false + }; + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::SKIP); +} + +// ---- Old region evacuation ---- + +// An old region at the garbage threshold with 50K live and 10K free, backed by +// ample shared reserves. Should be accepted as old evacuation. The old_evac +// budget consumes 50K * 1.4 = 70K for evacuation, and the promo budget absorbs +// the 10K of free bytes that are lost when this region enters the collection set. +TEST(ShenandoahGlobalCSet, old_region_accepted) { + auto budget = make_budget(5 * REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 50000, 10000, true, false + }; + + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::ADD_OLD_EVAC); + EXPECT_EQ(budget.old_evac.region_count(), (size_t)1); + EXPECT_EQ(budget.old_evac.live_bytes(), (size_t)50000); + EXPECT_EQ(budget.old_evac.consumed(), (size_t)(50000 * OLD_EVAC_WASTE)); + EXPECT_EQ(budget.promo.consumed(), (size_t)10000); + EXPECT_EQ(budget.cur_garbage(), GARBAGE_THRESHOLD); +} + +// An old region with zero shared reserves and no per-category reserves. Both +// the old_evac and promo reservations will fail, so the region is skipped. +// Verify that all budget state remains at zero (clean rollback). +TEST(ShenandoahGlobalCSet, old_region_rejected_no_budget) { + auto budget = make_budget(0); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 50000, 10000, true, false + }; + + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::SKIP); + EXPECT_EQ(budget.old_evac.region_count(), (size_t)0); + EXPECT_EQ(budget.committed_from_shared(), (size_t)0); + EXPECT_EQ(budget.old_evac.consumed(), (size_t)0); + EXPECT_EQ(budget.promo.consumed(), (size_t)0); +} + +// An old region with no per-category reserves but sufficient shared reserves. +// The old_evac budget must expand from the shared pool to accommodate the +// evacuation. Since old_evac starts at zero, all of its reserve comes from +// shared, so committed_from_shared should equal old_evac.reserve(). +TEST(ShenandoahGlobalCSet, old_region_expands_shared) { + auto budget = make_budget(5 * REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 50000, 0, true, false + }; + + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::ADD_OLD_EVAC); + EXPECT_GT(budget.committed_from_shared(), (size_t)0); + EXPECT_EQ(budget.committed_from_shared(), budget.old_evac.reserve()); + EXPECT_GE(budget.old_evac.reserve(), budget.old_evac.consumed()); +} + +// An old region with enough old_evac reserve for the live data but zero promo +// reserve, and a full region's worth of free bytes. The free bytes represent +// promotion capacity that is lost when this region enters the cset, so the +// promo budget must expand from shared reserves to absorb that loss. +TEST(ShenandoahGlobalCSet, old_region_free_bytes_consume_promo_reserve) { + size_t live_data = 10000; + size_t evac_needed = (size_t)(live_data * OLD_EVAC_WASTE); + auto budget = make_budget(5 * REGION_SIZE, 0, 0, evac_needed, 0); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, live_data, REGION_SIZE, true, false + }; + + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::ADD_OLD_EVAC); + EXPECT_GT(budget.committed_from_shared(), (size_t)0); + EXPECT_GE(budget.promo.reserve(), (size_t)REGION_SIZE); + EXPECT_EQ(budget.promo.consumed(), (size_t)REGION_SIZE); +} + +// ---- Promotion ---- + +// A young tenurable region at the garbage threshold with 40K live data and +// ample shared reserves. Should be accepted as a promotion. The promo budget +// consumes 40K * 1.2 = 48K. +TEST(ShenandoahGlobalCSet, tenurable_region_promoted) { + auto budget = make_budget(5 * REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 40000, 0, false, true + }; + + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::ADD_PROMO); + EXPECT_EQ(budget.promo.region_count(), (size_t)1); + EXPECT_EQ(budget.promo.live_bytes(), (size_t)40000); + EXPECT_EQ(budget.promo.consumed(), (size_t)(40000 * PROMO_EVAC_WASTE)); +} + +// A young tenurable region with zero reserves. The promo reservation fails, +// so the region is skipped. +TEST(ShenandoahGlobalCSet, tenurable_region_rejected) { + auto budget = make_budget(0); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 40000, 0, false, true + }; + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::SKIP); +} + +// ---- Young evacuation ---- + +// A young non-tenurable region at the garbage threshold with 30K live data. +// Should be accepted as a young evacuation. The young_evac budget consumes +// 30K * 1.2 = 36K. +TEST(ShenandoahGlobalCSet, young_region_evacuated) { + auto budget = make_budget(5 * REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 30000, 0, false, false + }; + + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::ADD_YOUNG_EVAC); + EXPECT_EQ(budget.young_evac.region_count(), (size_t)1); + EXPECT_EQ(budget.young_evac.consumed(), (size_t)(30000 * YOUNG_EVAC_WASTE)); +} + +// A young non-tenurable region with zero reserves. The young_evac reservation +// fails, so the region is skipped. +TEST(ShenandoahGlobalCSet, young_region_rejected) { + auto budget = make_budget(0); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 30000, 0, false, false + }; + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::SKIP); +} + +// ---- Multi-region and budget interaction tests ---- + +// Evaluate two identical young regions against a budget with only 1 region's +// worth of shared reserves. Each region has 150K live data, so anticipated +// consumption is 150K * 1.2 = 180K per region. The first region expands the +// young_evac reserve by one region (256K) from shared, consuming 180K of it. +// The second region needs another 180K, but only 76K remains in the reserve +// and the shared pool is exhausted, so it must be skipped. +TEST(ShenandoahGlobalCSet, shared_exhausted_across_regions) { + auto budget = make_budget(1 * REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 150000, 0, false, false + }; + + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::ADD_YOUNG_EVAC); + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::SKIP); + EXPECT_EQ(budget.young_evac.region_count(), (size_t)1); +} + +// An old region where the old_evac reserve is pre-sized to exactly cover the +// evacuation need, and the promo reserve covers the free bytes loss. No shared +// reserves should be drawn because the per-category reserves are sufficient. +TEST(ShenandoahGlobalCSet, existing_reserve_used_before_shared) { + size_t live_data = 50000; + size_t evac_needed = (size_t)(live_data * OLD_EVAC_WASTE); + auto budget = make_budget(5 * REGION_SIZE, 0, 0, evac_needed, REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, live_data, 1000, true, false + }; + + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::ADD_OLD_EVAC); + EXPECT_EQ(budget.committed_from_shared(), (size_t)0); +} + +// Evaluate two identical young regions and verify that cur_garbage accumulates +// the garbage from each accepted region. +TEST(ShenandoahGlobalCSet, garbage_accumulates) { + auto budget = make_budget(10 * REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 10000, 0, false, false + }; + + budget.try_add_region(region); + EXPECT_EQ(budget.cur_garbage(), GARBAGE_THRESHOLD); + budget.try_add_region(region); + EXPECT_EQ(budget.cur_garbage(), 2 * GARBAGE_THRESHOLD); +} + +// When no regions are evaluated, finish() should donate the entire shared +// reserve pool to the promo budget. +TEST(ShenandoahGlobalCSet, finish_donates_remaining_to_promo) { + auto budget = make_budget(5 * REGION_SIZE); + size_t promo_before = budget.promo.reserve(); + budget.finish(); + EXPECT_EQ(budget.promo.reserve(), promo_before + 5 * REGION_SIZE); +} + +// After evaluating one region that draws from the shared pool, finish() should +// donate only the remaining (unconsumed) shared reserves to promo. +TEST(ShenandoahGlobalCSet, finish_donates_remainder_after_use) { + auto budget = make_budget(5 * REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 10000, 0, false, false + }; + budget.try_add_region(region); + size_t shared_used = budget.committed_from_shared(); + size_t promo_before = budget.promo.reserve(); + budget.finish(); + EXPECT_EQ(budget.promo.reserve(), promo_before + (5 * REGION_SIZE - shared_used)); +} + +// ---- ShenandoahEvacuationBudget unit tests ---- + +// A reservation that fits entirely within the existing reserve should succeed +// without drawing from the shared pool. +TEST(ShenandoahEvacuationBudget, try_reserve_fits_without_expansion) { + ShenandoahSharedEvacReserve shared(5 * REGION_SIZE); + ShenandoahEvacuationBudget evac_budget(REGION_SIZE, 1.0, REGION_SIZE, &shared); + EXPECT_TRUE(evac_budget.try_reserve(REGION_SIZE)); + EXPECT_EQ(shared.committed, (size_t)0); + EXPECT_EQ(evac_budget.reserve(), (size_t)REGION_SIZE); +} + +// A reservation of REGION_SIZE+1 bytes starting from a zero reserve requires +// two region-sized expansions from the shared pool (one region isn't enough). +TEST(ShenandoahEvacuationBudget, try_reserve_expands_from_shared) { + ShenandoahSharedEvacReserve shared(5 * REGION_SIZE); + ShenandoahEvacuationBudget evac_budget(0, 1.0, REGION_SIZE, &shared); + EXPECT_TRUE(evac_budget.try_reserve(REGION_SIZE + 1)); + EXPECT_EQ(shared.committed, 2 * REGION_SIZE); + EXPECT_EQ(evac_budget.reserve(), 2 * REGION_SIZE); +} + +// A reservation with an empty shared pool should fail, leaving both the +// budget's reserve and the shared pool's committed count unchanged. +TEST(ShenandoahEvacuationBudget, try_reserve_fails_no_shared) { + ShenandoahSharedEvacReserve shared(0); + ShenandoahEvacuationBudget evac_budget(0, 1.0, REGION_SIZE, &shared); + EXPECT_FALSE(evac_budget.try_reserve(REGION_SIZE)); + EXPECT_EQ(shared.committed, (size_t)0); + EXPECT_EQ(evac_budget.reserve(), (size_t)0); +} + +// A reservation of 3 regions against a shared pool of only 2 regions should +// fail. On failure, neither the budget's reserve nor the shared pool's +// committed count should be modified. +TEST(ShenandoahEvacuationBudget, try_reserve_fails_insufficient_shared) { + ShenandoahSharedEvacReserve shared(2 * REGION_SIZE); + ShenandoahEvacuationBudget evac_budget(0, 1.0, REGION_SIZE, &shared); + EXPECT_FALSE(evac_budget.try_reserve(3 * REGION_SIZE)); + EXPECT_EQ(shared.committed, (size_t)0); + EXPECT_EQ(evac_budget.reserve(), (size_t)0); +} + +// Committing a consumption of 1200 bytes with 1000 live bytes should update +// all three tracking fields: consumed, live_bytes, and region_count. +TEST(ShenandoahEvacuationBudget, commit_updates_fields) { + ShenandoahSharedEvacReserve shared(5 * REGION_SIZE); + ShenandoahEvacuationBudget evac_budget(REGION_SIZE, 1.2, REGION_SIZE, &shared); + evac_budget.commit(1200, 1000); + EXPECT_EQ(evac_budget.consumed(), (size_t)1200); + EXPECT_EQ(evac_budget.live_bytes(), (size_t)1000); + EXPECT_EQ(evac_budget.region_count(), (size_t)1); +} diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahMarkBitMap.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahMarkBitMap.cpp index 3dbb7c62122..18cf3b3333f 100644 --- a/test/hotspot/gtest/gc/shenandoah/test_shenandoahMarkBitMap.cpp +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahMarkBitMap.cpp @@ -165,8 +165,8 @@ public: static bool run_test() { ShenandoahHeap* heap = ShenandoahHeap::heap(); - size_t heap_size = heap->max_capacity(); - size_t heap_size_words = heap_size / HeapWordSize; + size_t test_heap_size = MIN2(32 * M, heap->max_capacity()); + size_t heap_size_words = test_heap_size / HeapWordSize; HeapWord* my_heap_memory = heap->base(); HeapWord* end_of_my_heap = my_heap_memory + heap_size_words; MemRegion heap_descriptor(my_heap_memory, heap_size_words); @@ -175,7 +175,7 @@ public: _assertion_failures = 0; size_t bitmap_page_size = UseLargePages ? os::large_page_size() : os::vm_page_size(); - size_t bitmap_size_orig = ShenandoahMarkBitMap::compute_size(heap_size); + size_t bitmap_size_orig = ShenandoahMarkBitMap::compute_size(test_heap_size); size_t bitmap_size = align_up(bitmap_size_orig, bitmap_page_size); size_t bitmap_word_size = (bitmap_size + HeapWordSize - 1) / HeapWordSize; diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp index 4167e33b706..4633d8588d3 100644 --- a/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp @@ -26,6 +26,7 @@ #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/mode/shenandoahMode.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahPLAB.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #define SKIP_IF_NOT_SHENANDOAH() \ @@ -58,10 +59,15 @@ protected: old->set_evacuation_reserve(512 * HeapWordSize); Thread* thread = Thread::current(); - ShenandoahThreadLocalData::reset_plab_promoted(thread); - ShenandoahThreadLocalData::disable_plab_promotions(thread); - ShenandoahThreadLocalData::set_plab_actual_size(thread, INITIAL_PLAB_SIZE); - ShenandoahThreadLocalData::add_to_plab_promoted(thread, INITIAL_PLAB_PROMOTED); + ShenandoahPLAB* shenandoah_plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + if (shenandoah_plab == nullptr) { + ShenandoahThreadLocalData::initialize_gclab(thread); + shenandoah_plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + } + shenandoah_plab->reset_promoted(); + shenandoah_plab->disable_promotions(); + shenandoah_plab->set_actual_size(INITIAL_PLAB_SIZE); + shenandoah_plab->add_to_promoted(INITIAL_PLAB_PROMOTED); } void TearDown() override { @@ -72,15 +78,15 @@ protected: } static bool promotions_enabled() { - return ShenandoahThreadLocalData::allow_plab_promotions(Thread::current()); + return ShenandoahThreadLocalData::shenandoah_plab(Thread::current())->allows_promotion(); } static size_t plab_size() { - return ShenandoahThreadLocalData::get_plab_actual_size(Thread::current()); + return ShenandoahThreadLocalData::shenandoah_plab(Thread::current())->get_actual_size(); } static size_t plab_promoted() { - return ShenandoahThreadLocalData::get_plab_promoted(Thread::current()); + return ShenandoahThreadLocalData::shenandoah_plab(Thread::current())->get_promoted(); } }; diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index fd49050d022..641edaba4da 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -370,6 +370,32 @@ static void test_binary_instance_correctness_exhaustive(Operation op, Inference } } +template > +static void populate_sample_values(U* samples, const size_t sample_count, const InputType &input) { + constexpr size_t max_tries = 100; + constexpr size_t start_random_idx = 4; + + assert(sample_count >= 4, "need at least 4 slots for boundary values"); + samples[0] = U(input._lo); + samples[1] = U(input._hi); + samples[2] = input._ulo; + samples[3] = input._uhi; + + // Initialize remaining slots to a known-contained value in case the random + // fill below doesn't find enough values. + for (size_t i = start_random_idx; i < sample_count; i++) { + samples[i] = input._ulo; + } + + for (size_t tries = 0, idx = start_random_idx; tries < max_tries && idx < sample_count; tries++) { + U n = uniform_random(); + if (input.contains(n)) { + samples[idx] = n; + idx++; + } + } +} + // Check the correctness, that is, if v1 is an element of input1, v2 is an element of input2, then // op(v1, v2) must be an element of infer(input1, input2). This version does the check randomly on // a number of elements in input1 and input2. @@ -379,22 +405,11 @@ static void test_binary_instance_correctness_samples(Operation op, Inference inf auto result = infer(input1, input2); constexpr size_t sample_count = 6; - U input1_samples[sample_count] {U(input1._lo), U(input1._hi), input1._ulo, input1._uhi, input1._ulo, input1._ulo}; - U input2_samples[sample_count] {U(input2._lo), U(input2._hi), input2._ulo, input2._uhi, input2._ulo, input2._ulo}; + U input1_samples[sample_count]; + populate_sample_values(input1_samples, sample_count, input1); - auto random_sample = [](U* samples, const InputType& input) { - constexpr size_t max_tries = 100; - constexpr size_t start_random_idx = 4; - for (size_t tries = 0, idx = start_random_idx; tries < max_tries && idx < sample_count; tries++) { - U n = uniform_random(); - if (input.contains(n)) { - samples[idx] = n; - idx++; - } - } - }; - random_sample(input1_samples, input1); - random_sample(input2_samples, input2); + U input2_samples[sample_count]; + populate_sample_values(input2_samples, sample_count, input2); for (size_t i = 0; i < sample_count; i++) { for (size_t j = 0; j < sample_count; j++) { @@ -428,6 +443,22 @@ static void test_binary_instance_monotonicity_exhaustive(Inference infer, const } } +template +static InputType compute_random_superset(const InputType& input) { + using S = std::remove_const_t; + using U = std::remove_const_t; + + S lo = MIN2(input->_lo, S(uniform_random())); + S hi = MAX2(input->_hi, S(uniform_random())); + U ulo = MIN2(input->_ulo, uniform_random()); + U uhi = MAX2(input->_uhi, uniform_random()); + U zeros = input->_bits._zeros & uniform_random(); + U ones = input->_bits._ones & uniform_random(); + InputType super = InputType::make(TypeIntPrototype{{lo, hi}, {ulo, uhi}, {zeros, ones}}, 0); + assert(super.contains(input), "impossible"); + return super; +} + // Check the monotonicity, that is, if input1 is a subset of super1, input2 is a subset of super2, // then infer(input1, input2) must be a subset of infer(super1, super2). This version does the // check randomly on a number of supersets of input1 and input2. @@ -443,20 +474,8 @@ static void test_binary_instance_monotonicity_samples(Inference infer, const Inp ASSERT_TRUE(infer(input1, universe).contains(result)); ASSERT_TRUE(infer(universe, universe).contains(result)); - auto random_superset = [](const InputType& input) { - S lo = MIN2(input->_lo, S(uniform_random())); - S hi = MAX2(input->_hi, S(uniform_random())); - U ulo = MIN2(input->_ulo, uniform_random()); - U uhi = MAX2(input->_uhi, uniform_random()); - U zeros = input->_bits._zeros & uniform_random(); - U ones = input->_bits._ones & uniform_random(); - InputType super = InputType::make(TypeIntPrototype{{lo, hi}, {ulo, uhi}, {zeros, ones}}, 0); - assert(super.contains(input), "impossible"); - return super; - }; - - InputType super1 = random_superset(input1); - InputType super2 = random_superset(input2); + InputType super1 = compute_random_superset(input1); + InputType super2 = compute_random_superset(input2); ASSERT_TRUE(infer(super1, input2).contains(result)); ASSERT_TRUE(infer(input1, super2).contains(result)); ASSERT_TRUE(infer(super1, super2).contains(result)); @@ -480,16 +499,11 @@ static void test_binary_exhaustive(Operation op, Inference infer) { } } -// Verify the correctness and monotonicity of an inference function by randomly sampling instances -// of InputType -template -static void test_binary_random(Operation op, Inference infer) { +template +static void populate_sample_types(InputType* samples, const size_t sample_count) { using S = std::remove_const_t; using U = std::remove_const_t; - constexpr size_t sample_count = 100; - InputType samples[sample_count]; - // Fill with {0} for (size_t i = 0; i < sample_count; i++) { samples[i] = InputType::make(TypeIntPrototype{{S(0), S(0)}, {U(0), U(0)}, {U(0), U(0)}}, 0); @@ -549,6 +563,18 @@ static void test_binary_random(Operation op, Inference infer) { canonicalized_t._data._bits}; idx++; } +} + +// Verify the correctness and monotonicity of an inference function by randomly sampling instances +// of InputType +template +static void test_binary_random(Operation op, Inference infer) { + using S = std::remove_const_t; + using U = std::remove_const_t; + + constexpr size_t sample_count = 100; + InputType samples[sample_count]; + populate_sample_types(samples, sample_count); for (size_t i = 0; i < sample_count; i++) { for (size_t j = 0; j < sample_count; j++) { @@ -570,6 +596,107 @@ static void test_binary() { test_binary_random>(Operation(), Inference>()); } +template +static U lshift_op(U v, int shift) { + return v << shift; +} + +// Test correctness: for all values v in input, (v << shift) must be in the result +template +static void test_lshift_correctness(const InputType& input, int shift) { + using U = std::remove_const_t_ulo)>; + auto result = RangeInference::infer_lshift(input, shift); + for (juint v = 0; v <= juint(std::numeric_limits::max()); v++) { + if (input.contains(U(v))) { + U r = lshift_op(U(v), shift); + ASSERT_TRUE(result.contains(r)); + } + } +} + +// Test correctness for jint/jlong types by sampling values from the input range. +template +static void test_lshift_correctness_samples(const InputType& input, int shift) { + using U = std::remove_const_t_ulo)>; + auto result = RangeInference::infer_lshift(input, shift); + + constexpr size_t sample_count = 6; + U samples[sample_count]; + populate_sample_values(samples, sample_count, input); + + for (size_t i = 0; i < sample_count; i++) { + ASSERT_TRUE(input.contains(samples[i])); + + U r = lshift_op(samples[i], shift); + ASSERT_TRUE(result.contains(r)); + } +} + +// Test monotonicity: if input is a subset of super, then result is a subset of +// infer_lshift(super, shift) +template +static void test_lshift_monotonicity(const InputType& input, int shift) { + auto result = RangeInference::infer_lshift(input, shift); + for (const InputType& super : all_instances()) { + if (super.contains(input)) { + ASSERT_TRUE(RangeInference::infer_lshift(super, shift).contains(result)); + } + } +} + +// Test monotonicity for jint/jlong types by constructing random supersets. +template +static void test_lshift_monotonicity_samples(const InputType& input, int shift) { + using S = std::remove_const_t_lo)>; + using U = std::remove_const_t_ulo)>; + auto result = RangeInference::infer_lshift(input, shift); + + // The universe is a superset of all other sets + InputType universe = InputType{std::numeric_limits::min(), std::numeric_limits::max(), U(0), U(-1), {U(0), U(0)}}; + ASSERT_TRUE(RangeInference::infer_lshift(universe, shift).contains(result)); + + InputType super = compute_random_superset(input); + ASSERT_TRUE(RangeInference::infer_lshift(super, shift).contains(result)); +} + +template +static void test_lshift_for_type() { + using U = std::remove_const_t; + constexpr int type_bits = HotSpotNumerics::type_width(); + for (const InputType& input : all_instances()) { + for (int shift = 0; shift < type_bits; shift++) { + test_lshift_correctness(input, shift); + test_lshift_monotonicity(input, shift); + } + } +} + +// Sample-based test for jint/jlong types +template +static void test_lshift_random() { + using S = std::remove_const_t; + using U = std::remove_const_t; + + constexpr size_t sample_count = 100; + InputType samples[sample_count]; + populate_sample_types(samples, sample_count); + + for (size_t i = 0; i < sample_count; i++) { + for (int shift = 0; shift < HotSpotNumerics::type_width(); shift++) { + test_lshift_correctness_samples(samples[i], shift); + test_lshift_monotonicity_samples(samples[i], shift); + } + } +} + +static void test_lshift() { + test_lshift_for_type, uintn_t<1>>>(); + test_lshift_for_type, uintn_t<2>>>(); + test_lshift_for_type, uintn_t<3>>>(); + test_lshift_random>(); + test_lshift_random>(); +} + template class OpAnd { public: @@ -622,4 +749,5 @@ TEST(opto, range_inference) { test_binary(); test_binary(); test_binary(); + test_lshift(); } diff --git a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp index 7a4f7bcb99e..69d04ae8883 100644 --- a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp +++ b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp @@ -479,7 +479,7 @@ TEST(cgroupTest, set_cgroupv1_subsystem_path_adjusted) { ccc->set_subsystem_path((char*)cpu.cgroup_path); EXPECT_TRUE(ccc->needs_hierarchy_adjustment()); - CgroupUtil::adjust_controller(ccc); + CgroupUtil::adjust_controller(ccc, 1); ASSERT_STREQ(cpu.expected_path, ccc->subsystem_path()); EXPECT_FALSE(ccc->needs_hierarchy_adjustment()); @@ -489,7 +489,7 @@ TEST(cgroupTest, set_cgroupv1_subsystem_path_adjusted) { cmc->set_subsystem_path((char*)memory.cgroup_path); EXPECT_TRUE(cmc->needs_hierarchy_adjustment()); - CgroupUtil::adjust_controller(cmc); + CgroupUtil::adjust_controller(cmc, (physical_memory_size_type)1024); ASSERT_STREQ(memory.expected_path, cmc->subsystem_path()); EXPECT_FALSE(cmc->needs_hierarchy_adjustment()); } @@ -512,7 +512,7 @@ TEST(cgroupTest, set_cgroupv2_subsystem_path_adjusted) { true /* read-only mount */)); EXPECT_TRUE(ccc->needs_hierarchy_adjustment()); - CgroupUtil::adjust_controller(ccc); + CgroupUtil::adjust_controller(ccc, 1); ASSERT_STREQ(cpu.expected_path, ccc->subsystem_path()); EXPECT_FALSE(ccc->needs_hierarchy_adjustment()); @@ -521,7 +521,7 @@ TEST(cgroupTest, set_cgroupv2_subsystem_path_adjusted) { true /* read-only mount */)); EXPECT_TRUE(cmc->needs_hierarchy_adjustment()); - CgroupUtil::adjust_controller(cmc); + CgroupUtil::adjust_controller(cmc, (physical_memory_size_type)1024); ASSERT_STREQ(memory.expected_path, cmc->subsystem_path()); EXPECT_FALSE(cmc->needs_hierarchy_adjustment()); } diff --git a/test/hotspot/gtest/runtime/test_globals.cpp b/test/hotspot/gtest/runtime/test_globals.cpp index 9ef5bd6a5af..e88f930ff73 100644 --- a/test/hotspot/gtest/runtime/test_globals.cpp +++ b/test/hotspot/gtest/runtime/test_globals.cpp @@ -42,7 +42,7 @@ } while (0) TEST_VM(FlagGuard, bool_flag) { - TEST_FLAG(AlwaysActAsServerClassMachine, bool, true); + TEST_FLAG(PrintCompilation, bool, true); } TEST_VM(FlagGuard, int_flag) { diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index ba63f775223..dec9682f0cf 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -81,7 +81,7 @@ compiler/interpreter/Test6833129.java 8335266 generic-i586 compiler/c2/aarch64/TestStaticCallStub.java 8359963 linux-aarch64,macosx-aarch64 -compiler/longcountedloops/TestLoopNestTooManyTraps.java 8376591 generic-all +compiler/unsafe/AlignmentGapAccess.java 8373487 generic-all ############################################################################# @@ -115,9 +115,8 @@ runtime/NMT/VirtualAllocCommitMerge.java 8309698 linux-s390x applications/jcstress/copy.java 8229852 linux-all -containers/docker/TestJcmd.java 8278102 linux-all containers/docker/TestJFREvents.java 8327723 linux-x64 -containers/docker/TestJcmdWithSideCar.java 8341518 linux-x64 +containers/docker/TestJcmdWithSideCar.java 8341518 linux-all ############################################################################# @@ -196,4 +195,3 @@ vmTestbase/nsk/monitoring/ThreadMXBean/findMonitorDeadlockedThreads/find006/Test # in either implementation or test code. ############################################################################# - diff --git a/test/hotspot/jtreg/TEST.ROOT b/test/hotspot/jtreg/TEST.ROOT index 2a3d3eebbbd..892529b966f 100644 --- a/test/hotspot/jtreg/TEST.ROOT +++ b/test/hotspot/jtreg/TEST.ROOT @@ -69,7 +69,6 @@ requires.properties= \ vm.gc.Z \ vm.jvmci \ vm.jvmci.enabled \ - vm.emulatedClient \ vm.cpu.features \ vm.pageSize \ vm.debug \ diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups index 32cff812975..6e9421e5c09 100644 --- a/test/hotspot/jtreg/TEST.groups +++ b/test/hotspot/jtreg/TEST.groups @@ -458,6 +458,7 @@ hotspot_appcds_dynamic = \ -runtime/cds/appcds/TestEpsilonGCWithCDS.java \ -runtime/cds/appcds/TestParallelGCWithCDS.java \ -runtime/cds/appcds/TestSerialGCWithCDS.java \ + -runtime/cds/appcds/TestZGCWithAOTHeap.java \ -runtime/cds/appcds/TestZGCWithCDS.java \ -runtime/cds/appcds/UnusedCPDuringDump.java \ -runtime/cds/appcds/VerifierTest_1B.java diff --git a/test/hotspot/jtreg/compiler/arraycopy/TestArrayCopyNoInitDeopt.java b/test/hotspot/jtreg/compiler/arraycopy/TestArrayCopyNoInitDeopt.java index 839ff6cf36e..9e920a08b0f 100644 --- a/test/hotspot/jtreg/compiler/arraycopy/TestArrayCopyNoInitDeopt.java +++ b/test/hotspot/jtreg/compiler/arraycopy/TestArrayCopyNoInitDeopt.java @@ -25,7 +25,7 @@ * @test * @bug 8072016 * @summary Infinite deoptimization/recompilation cycles in case of arraycopy with tightly coupled allocation - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management @@ -87,8 +87,8 @@ public class TestArrayCopyNoInitDeopt { } static public void main(String[] args) throws Exception { - if (!Platform.isServer() || Platform.isEmulatedClient()) { - throw new Error("TESTBUG: Not server mode"); + if (!Platform.isServer()) { + throw new Error("TESTBUG: Not server VM"); } // Only execute if C2 is available if (TIERED_STOP_AT_LEVEL == CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) { diff --git a/test/hotspot/jtreg/compiler/arraycopy/TestDefaultMethodArrayCloneDeoptC2.java b/test/hotspot/jtreg/compiler/arraycopy/TestDefaultMethodArrayCloneDeoptC2.java index 7f5a891ce9e..c7554dcffdb 100644 --- a/test/hotspot/jtreg/compiler/arraycopy/TestDefaultMethodArrayCloneDeoptC2.java +++ b/test/hotspot/jtreg/compiler/arraycopy/TestDefaultMethodArrayCloneDeoptC2.java @@ -27,7 +27,7 @@ * @summary C2: Access to [].clone from interfaces fails. * @library /test/lib / * - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -Xcomp -Xbatch -Xbootclasspath/a:. -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSetPreselector.hpp b/test/hotspot/jtreg/compiler/c2/TestAllocatePrefetchStyle.java similarity index 53% rename from src/hotspot/share/gc/shenandoah/shenandoahCollectionSetPreselector.hpp rename to test/hotspot/jtreg/compiler/c2/TestAllocatePrefetchStyle.java index b78259dd85b..53763302f90 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSetPreselector.hpp +++ b/test/hotspot/jtreg/compiler/c2/TestAllocatePrefetchStyle.java @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * Copyright (c) 2026 IBM Corporation. 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 @@ -19,33 +19,26 @@ * 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. - * */ -#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHCOLLECTIONSETPRESELECTOR_HPP -#define SHARE_GC_SHENANDOAH_SHENANDOAHCOLLECTIONSETPRESELECTOR_HPP +/* + * @test + * @bug 8379180 + * @summary test AllocatePrefetchStyle + * + * @run main/othervm -Xbatch -XX:AllocatePrefetchStyle=2 compiler.c2.TestAllocatePrefetchStyle + * @run main/othervm -Xbatch -XX:AllocatePrefetchStyle=3 compiler.c2.TestAllocatePrefetchStyle + */ +package compiler.c2; -#include "gc/shenandoah/shenandoahCollectionSet.hpp" -#include "memory/resourceArea.hpp" - -class ShenandoahCollectionSetPreselector : public StackObj { - ShenandoahCollectionSet* _cset; - bool* _pset; - ResourceMark _rm; - -public: - ShenandoahCollectionSetPreselector(ShenandoahCollectionSet* cset, size_t num_regions): - _cset(cset) { - _pset = NEW_RESOURCE_ARRAY(bool, num_regions); - for (unsigned int i = 0; i < num_regions; i++) { - _pset[i] = false; +public class TestAllocatePrefetchStyle { + public static void main(String[] args) { + for (int i = 0; i < 20_000; i++) { + test(); + } } - _cset->establish_preselected(_pset); - } - ~ShenandoahCollectionSetPreselector() { - _cset->abandon_preselected(); - } -}; - -#endif // SHARE_GC_SHENANDOAH_SHENANDOAHCOLLECTIONSETPRESELECTOR_HPP + private static int[] test() { + return new int[10]; + } +} diff --git a/test/hotspot/jtreg/compiler/c2/TestAllocatePrefetchStyleLargeFlags.java b/test/hotspot/jtreg/compiler/c2/TestAllocatePrefetchStyleLargeFlags.java new file mode 100644 index 00000000000..b455107d5f6 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestAllocatePrefetchStyleLargeFlags.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026, 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 Stress allocation prefetch with large legal AllocatePrefetch* values + * @requires vm.compiler2.enabled + * + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:+UseTLAB + * -XX:AllocatePrefetchStyle=1 + * -XX:AllocatePrefetchDistance=512 + * -XX:AllocatePrefetchStepSize=512 + * -XX:AllocatePrefetchLines=64 + * -XX:AllocateInstancePrefetchLines=64 + * compiler.c2.TestAllocatePrefetchStyleLargeFlags + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:+UseTLAB + * -XX:AllocatePrefetchStyle=2 + * -XX:AllocatePrefetchDistance=512 + * -XX:AllocatePrefetchStepSize=512 + * -XX:AllocatePrefetchLines=64 + * -XX:AllocateInstancePrefetchLines=64 + * compiler.c2.TestAllocatePrefetchStyleLargeFlags + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:+UseTLAB + * -XX:AllocatePrefetchStyle=3 + * -XX:AllocatePrefetchDistance=512 + * -XX:AllocatePrefetchStepSize=512 + * -XX:AllocatePrefetchLines=64 + * -XX:AllocateInstancePrefetchLines=64 + * compiler.c2.TestAllocatePrefetchStyleLargeFlags + */ + +package compiler.c2; + +public class TestAllocatePrefetchStyleLargeFlags { + private static volatile Object sink; + + private static final class Payload { + private final int value; + + private Payload(int value) { + this.value = value; + } + } + + private static Object allocateInstance(int value) { + return new Payload(value); + } + + private static Object allocateArray(int value) { + return new int[value & 31]; + } + + public static void main(String[] args) { + for (int i = 0; i < 50_000; i++) { + sink = allocateInstance(i); + sink = allocateArray(i); + } + } +} diff --git a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndHeapDump.java b/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndHeapDump.java index 9319da0e0b9..f65643f8d87 100644 --- a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndHeapDump.java +++ b/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndHeapDump.java @@ -26,7 +26,7 @@ * @bug 8319784 * @summary Check that the JVM is able to dump the heap even when there are ReduceAllocationMerge in the scope. * @library /test/lib / - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @run main/othervm compiler.c2.TestReduceAllocationAndHeapDump */ diff --git a/test/hotspot/jtreg/compiler/c2/gvn/CmpUNodeValueTests.java b/test/hotspot/jtreg/compiler/c2/gvn/CmpUNodeValueTests.java index cbcbda3422f..2df74e52971 100644 --- a/test/hotspot/jtreg/compiler/c2/gvn/CmpUNodeValueTests.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/CmpUNodeValueTests.java @@ -99,7 +99,7 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, counts = {IRNode.CMP_U, "1"}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {IRNode.CMP_U, "1"}) int testIntControl(boolean b1, boolean b2) { int v1 = b1 ? 1 : -1; int v2 = b2 ? 1 : 0; @@ -107,7 +107,7 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) int testIntLT(int x) { int v1 = x & Integer.MAX_VALUE; // Unset the highest bit, make v1 non-negative int v2 = Integer.MIN_VALUE; @@ -115,7 +115,7 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) int testIntLE(boolean b1, boolean b2) { int v1 = b1 ? 2 : 0; int v2 = b2 ? -1 : 2; @@ -123,7 +123,7 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) int testIntGT(boolean b1, boolean b2) { int v1 = b1 ? Integer.MIN_VALUE : Integer.MAX_VALUE; int v2 = b2 ? 0 : 2; @@ -131,7 +131,7 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) int testIntGE(int x, int y) { int v1 = x | Integer.MIN_VALUE; // Set the highest bit, make v1 negative int v2 = y & Integer.MAX_VALUE; // Unset the highest bit, make v2 non-negative @@ -139,13 +139,13 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) int testIntEQ() { return Integer.compareUnsigned(oneInline(), 1) == 0 ? 0 : one(); } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) int testIntNE(boolean b1, boolean b2) { int v1 = b1 ? 1 : -1; int v2 = b2 ? 0 : 2; @@ -153,7 +153,7 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, counts = {IRNode.CMP_UL, "1"}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {IRNode.CMP_UL, "1"}) int testLongControl(boolean b1, boolean b2) { long v1 = b1 ? 1 : -1; long v2 = b2 ? 1 : 0; @@ -162,7 +162,7 @@ public class CmpUNodeValueTests { @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) int testLongLT(int x, boolean b2) { long v1 = Integer.toUnsignedLong(x); long v2 = Integer.MIN_VALUE; @@ -170,7 +170,7 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) int testLongLE(boolean b1, boolean b2) { long v1 = b1 ? 2 : 0; long v2 = b2 ? -1 : 2; @@ -178,7 +178,7 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) int testLongGT(boolean b1, boolean b2) { long v1 = b1 ? Long.MIN_VALUE : Long.MAX_VALUE; long v2 = b2 ? 0 : 2; @@ -186,7 +186,7 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) int testLongGE(int x, int y) { long v1 = x | Long.MIN_VALUE; // Set the highest bit, make v1 negative long v2 = y & Long.MAX_VALUE; // Unset the highest bit, make v2 non-negative @@ -194,13 +194,13 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) int testLongEQ() { return Long.compareUnsigned(oneInline(), 1L) == 0 ? 0 : one(); } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) int testLongNE(boolean b1, boolean b2) { long v1 = b1 ? 1 : -1; long v2 = b2 ? 0 : 2; diff --git a/test/hotspot/jtreg/compiler/c2/gvn/MissedURShiftIAddILShiftIdeal.java b/test/hotspot/jtreg/compiler/c2/gvn/MissedURShiftIAddILShiftIdeal.java index dd446d8bb25..a953ea3bcca 100644 --- a/test/hotspot/jtreg/compiler/c2/gvn/MissedURShiftIAddILShiftIdeal.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/MissedURShiftIAddILShiftIdeal.java @@ -30,7 +30,7 @@ import java.util.Random; /* * @test - * @bug 8378413 + * @bug 8378413 8379544 * @key randomness * @summary Verify that URShift{I,L}Node::Ideal optimizes ((x << C) + y) >>> C * regardless of Add input order, i.e. it is commutative w.r.t. the addition. @@ -50,8 +50,8 @@ public class MissedURShiftIAddILShiftIdeal { framework.start(); } - @Run(test = {"testI", "testICommuted", "testIComputedY", - "testL", "testLCommuted", "testLComputedY"}) + @Run(test = {"testI", "testICommuted", "testIComputedY", "testIComputedX", + "testL", "testLCommuted", "testLComputedY", "testLComputedX"}) public void runMethod() { int xi = RANDOM.nextInt(); int yi = RANDOM.nextInt(); @@ -71,6 +71,7 @@ public class MissedURShiftIAddILShiftIdeal { Asserts.assertEQ(((x << 3) + y) >>> 3, testI(x, y)); Asserts.assertEQ((y + (x << 5)) >>> 5, testICommuted(x, y)); Asserts.assertEQ(((x << 7) + (a ^ b)) >>> 7, testIComputedY(x, a, b)); + Asserts.assertEQ((((a ^ b) << 19) + y) >>> 19, testIComputedX(a, b, y)); } @DontCompile @@ -78,6 +79,7 @@ public class MissedURShiftIAddILShiftIdeal { Asserts.assertEQ(((x << 9) + y) >>> 9, testL(x, y)); Asserts.assertEQ((y + (x << 11)) >>> 11, testLCommuted(x, y)); Asserts.assertEQ(((x << 13) + (a ^ b)) >>> 13, testLComputedY(x, a, b)); + Asserts.assertEQ((((a ^ b) << 19) + y) >>> 19, testLComputedX(a, b, y)); } @Test @@ -135,4 +137,39 @@ public class MissedURShiftIAddILShiftIdeal { static long testLComputedY(long x, long a, long b) { return ((x << 13) + (a ^ b)) >>> 13; } + + @Test + // (((a ^ b) << v) + y) >>> 19 => ((a ^ b) + (y >>> 19)) & mask + // v is only known to be 19 after loop optimization, so LShiftI's shift count + // changes mid-IGVN. URShiftI must be notified through AddI (Case 2). + @IR(counts = {IRNode.LSHIFT_I, "0", + IRNode.URSHIFT_I, "1", + IRNode.AND_I, "1"}) + static int testIComputedX(int a, int b, int y) { + int u = 19; + int v = 0; + do { + u--; + v++; + } while (u > 0); + return (((a ^ b) << v) + y) >>> 19; + } + + @Test + // (((a ^ b) << v) + y) >>> 19 => ((a ^ b) + (y >>> 19)) & mask + // v is only known to be 19 after loop optimization, so LShiftL's shift count + // changes mid-IGVN. URShiftL must be notified through AddL (Case 2). + @IR(counts = {IRNode.LSHIFT_L, "0", + IRNode.URSHIFT_L, "1", + IRNode.AND_L, "1"}) + static long testLComputedX(long a, long b, long y) { + int u = 19; + int v = 0; + do { + u--; + v++; + } while (u > 0); + return (((a ^ b) << v) + y) >>> 19; + } + } diff --git a/test/hotspot/jtreg/compiler/c2/igvn/TestURShiftAddNotification.java b/test/hotspot/jtreg/compiler/c2/igvn/TestURShiftAddNotification.java new file mode 100644 index 00000000000..8ca192ea38b --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/igvn/TestURShiftAddNotification.java @@ -0,0 +1,89 @@ +/* + * Copyright Amazon.com Inc. 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.c2.igvn; + +import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; +import jdk.test.lib.Platform; +import jdk.test.lib.Utils; +import java.util.Random; + +/* + * @test + * @bug 8379460 + * @key randomness + * @summary When AddI/AddL inputs change during IGVN, URShift users must be re-added + * to the IGVN worklist so they can re-check the ((X << z) + Y) >>> z optimization. + * @library /test/lib / + * @run driver ${test.main.class} + */ +public class TestURShiftAddNotification { + + private static final Random RANDOM = Utils.getRandomInstance(); + + public static void main(String[] args) { + var framework = new TestFramework(); + framework.addScenarios(new Scenario(0)); + if (Platform.isDebugBuild()) { + framework.addScenarios(new Scenario(1, "-XX:VerifyIterativeGVN=1110")); + } + framework.start(); + } + + // The trick: a loop whose exit value is only known after loop optimization. + // During initial GVN, i is a Phi, so (x << C) * i stays as MulI — URShift + // can't see the LShiftI input through the MulI. After loop opts resolve + // i = 1, MulI identity-folds to LShiftI (same type, no cascade), and + // without the fix URShift is never re-notified about the new LShiftI input. + + @Run(test = {"testI", "testL"}) + public void runTests() { + int xi = RANDOM.nextInt(); + int yi = RANDOM.nextInt(); + long xl = RANDOM.nextLong(); + long yl = RANDOM.nextLong(); + + Asserts.assertEQ(((xi << 3) + yi) >>> 3, testI(xi, yi)); + Asserts.assertEQ(((xl << 9) + yl) >>> 9, testL(xl, yl)); + } + + @Test + @IR(failOn = {IRNode.LSHIFT_I, IRNode.MUL_I}, + counts = {IRNode.URSHIFT_I, "1", IRNode.AND_I, "1"}) + static int testI(int x, int y) { + int i; + for (i = -10; i < 1; i++) { } + int c = (x << 3) * i; + return (c + y) >>> 3; + } + + @Test + @IR(failOn = {IRNode.LSHIFT_L, IRNode.MUL_L}, + counts = {IRNode.URSHIFT_L, "1", IRNode.AND_L, "1"}) + static long testL(long x, long y) { + int i; + for (i = -10; i < 1; i++) { } + long c = (x << 9) * i; + return (c + y) >>> 9; + } +} diff --git a/test/hotspot/jtreg/compiler/c2/irTests/ModDNodeTests.java b/test/hotspot/jtreg/compiler/c2/irTests/ModDNodeTests.java index 6b20b782923..ac427ea3831 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/ModDNodeTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/ModDNodeTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -29,7 +29,7 @@ import compiler.lib.ir_framework.*; /* * @test - * @bug 8345766 + * @bug 8345766 8378742 * @key randomness * @summary Test that Ideal transformations of ModDNode are being performed as expected. * @library /test/lib / @@ -37,6 +37,7 @@ import compiler.lib.ir_framework.*; */ public class ModDNodeTests { public static final double q = Utils.getRandomInstance().nextDouble() * 100.0d; + public static volatile int volatileField; public static void main(String[] args) { TestFramework.run(); @@ -48,6 +49,7 @@ public class ModDNodeTests { "unusedResultAfterLoopOpt1", "unusedResultAfterLoopOpt2", "unusedResultAfterLoopOpt3", + "constantFoldInCCP" }) public void runMethod() { Asserts.assertEQ(constant(), q % 72.0d % 30.0d); @@ -61,6 +63,7 @@ public class ModDNodeTests { Asserts.assertEQ(unusedResultAfterLoopOpt1(1.1d, 2.2d), 0.d); Asserts.assertEQ(unusedResultAfterLoopOpt2(1.1d, 2.2d), 0.d); Asserts.assertEQ(unusedResultAfterLoopOpt3(1.1d, 2.2d), 0.d); + Asserts.assertEQ(constantFoldInCCP(), 4.0d); } // Note: we used to check for ConD nodes in the IR. But that is a bit brittle: @@ -72,7 +75,7 @@ public class ModDNodeTests { @Test @IR(counts = {IRNode.MOD_D, "2"}, phase = CompilePhase.AFTER_PARSING) - @IR(counts = {".*CallLeaf.*drem.*", "0"}, + @IR(failOn = {".*CallLeaf.*drem.*"}, phase = CompilePhase.BEFORE_MATCHING) public double constant() { // All constants available during parsing @@ -84,7 +87,7 @@ public class ModDNodeTests { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_D, "1"}, phase = CompilePhase.PHASEIDEALLOOP1) // Only constant fold after some loop opts - @IR(counts = {".*CallLeaf.*drem.*", "0"}, + @IR(failOn = {".*CallLeaf.*drem.*"}, phase = CompilePhase.BEFORE_MATCHING) public double alsoConstant() { // Make sure value is only available after second loop opts round @@ -102,7 +105,7 @@ public class ModDNodeTests { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_D, "2"}, phase = CompilePhase.PHASEIDEALLOOP1) // Only constant fold after some loop opts - @IR(counts = {".*CallLeaf.*drem.*", "0"}, + @IR(failOn = {".*CallLeaf.*drem.*"}, phase = CompilePhase.BEFORE_MATCHING) public double nanLeftConstant() { // Make sure value is only available after second loop opts round @@ -120,7 +123,7 @@ public class ModDNodeTests { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_D, "2"}, phase = CompilePhase.PHASEIDEALLOOP1) // Only constant fold after some loop opts - @IR(counts = {".*CallLeaf.*drem.*", "0"}, + @IR(failOn = {".*CallLeaf.*drem.*"}, phase = CompilePhase.BEFORE_MATCHING) public double nanRightConstant() { // Make sure value is only available after second loop opts round @@ -156,7 +159,7 @@ public class ModDNodeTests { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_D, "0"}, phase = CompilePhase.ITER_GVN1) // IGVN removes unused nodes - @IR(counts = {".*CallLeaf.*drem.*", "0"}, + @IR(failOn = {".*CallLeaf.*drem.*"}, phase = CompilePhase.BEFORE_MATCHING) public void unusedResult(double x, double y) { double unused = x % y; @@ -165,9 +168,9 @@ public class ModDNodeTests { @Test @IR(counts = {IRNode.MOD_D, "1"}, phase = CompilePhase.AFTER_PARSING) - @IR(counts = {IRNode.MOD_D, "0"}, + @IR(failOn = {IRNode.MOD_D}, phase = CompilePhase.ITER_GVN1) // IGVN removes unused nodes - @IR(counts = {".*CallLeaf.*drem.*", "0"}, + @IR(failOn = {".*CallLeaf.*drem.*"}, phase = CompilePhase.BEFORE_MATCHING) public void repeatedlyUnused(double x, double y) { double unused = 1.d; @@ -185,9 +188,9 @@ public class ModDNodeTests { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_D, "1"}, phase = CompilePhase.ITER_GVN2) - @IR(counts = {IRNode.MOD_D, "0"}, + @IR(failOn = {IRNode.MOD_D}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) - @IR(counts = {".*CallLeaf.*drem.*", "0"}, + @IR(failOn = {".*CallLeaf.*drem.*"}, phase = CompilePhase.BEFORE_MATCHING) public double unusedResultAfterLoopOpt1(double x, double y) { double unused = x % y; @@ -210,9 +213,9 @@ public class ModDNodeTests { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_D, "1"}, phase = CompilePhase.AFTER_CLOOPS) - @IR(counts = {IRNode.MOD_D, "0"}, + @IR(failOn = {IRNode.MOD_D}, phase = CompilePhase.PHASEIDEALLOOP1) - @IR(counts = {".*CallLeaf.*drem.*", "0"}, + @IR(failOn = {".*CallLeaf.*drem.*"}, phase = CompilePhase.BEFORE_MATCHING) public double unusedResultAfterLoopOpt2(double x, double y) { int a = 77; @@ -235,9 +238,9 @@ public class ModDNodeTests { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_D, "2"}, phase = CompilePhase.AFTER_CLOOPS) // drop the useless one - @IR(counts = {IRNode.MOD_D, "0"}, + @IR(failOn = {IRNode.MOD_D}, phase = CompilePhase.PHASEIDEALLOOP1) // drop the rest - @IR(counts = {".*CallLeaf.*drem.*", "0"}, + @IR(failOn = {".*CallLeaf.*drem.*"}, phase = CompilePhase.BEFORE_MATCHING) public double unusedResultAfterLoopOpt3(double x, double y) { double unused = x % y; @@ -252,4 +255,25 @@ public class ModDNodeTests { int other = (b - 77) * (int)(x % y % 1.d); return (double)other; } + + @Test + @IR(failOn = {IRNode.CMP_D}, + phase = CompilePhase.CCP1) + @IR(failOn = {IRNode.MOD_D}, + phase = CompilePhase.BEFORE_MACRO_EXPANSION) + public double constantFoldInCCP(){ + int i; + for (i = 2; i < 4; i *= 2) { + } + int j; + for (j = 2; j < 4; j *= 2) { + } + volatileField = 42; + double v1 = (double) i / 2; + double v2 = j; + double v = v1 % v2; + for (; v < v2; v *= 2) { + } + return v; + } } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/ModFNodeTests.java b/test/hotspot/jtreg/compiler/c2/irTests/ModFNodeTests.java index 2f69578c2f0..f703df3d04e 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/ModFNodeTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/ModFNodeTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -29,7 +29,7 @@ import compiler.lib.ir_framework.*; /* * @test - * @bug 8345766 + * @bug 8345766 8378742 * @key randomness * @summary Test that Ideal transformations of ModFNode are being performed as expected. * @library /test/lib / @@ -37,6 +37,7 @@ import compiler.lib.ir_framework.*; */ public class ModFNodeTests { public static final float q = Utils.getRandomInstance().nextFloat() * 100.0f; + public static volatile int volatileField; public static void main(String[] args) { TestFramework.run(); @@ -48,6 +49,7 @@ public class ModFNodeTests { "unusedResultAfterLoopOpt1", "unusedResultAfterLoopOpt2", "unusedResultAfterLoopOpt3", + "constantFoldInCCP" }) public void runMethod() { Asserts.assertEQ(constant(), q % 72.0f % 30.0f); @@ -61,6 +63,7 @@ public class ModFNodeTests { Asserts.assertEQ(unusedResultAfterLoopOpt1(1.1f, 2.2f), 0.f); Asserts.assertEQ(unusedResultAfterLoopOpt2(1.1f, 2.2f), 0.f); Asserts.assertEQ(unusedResultAfterLoopOpt3(1.1f, 2.2f), 0.f); + Asserts.assertEQ(constantFoldInCCP(), 4.0f); } // Note: we used to check for ConF nodes in the IR. But that is a bit brittle: @@ -72,7 +75,7 @@ public class ModFNodeTests { @Test @IR(counts = {IRNode.MOD_F, "2"}, phase = CompilePhase.AFTER_PARSING) - @IR(counts = {".*CallLeaf.*frem.*", "0"}, + @IR(failOn = {".*CallLeaf.*frem.*"}, phase = CompilePhase.BEFORE_MATCHING) public float constant() { // All constants available during parsing @@ -84,7 +87,7 @@ public class ModFNodeTests { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_F, "1"}, phase = CompilePhase.PHASEIDEALLOOP1) // Only constant fold after some loop opts - @IR(counts = {".*CallLeaf.*frem.*", "0"}, + @IR(failOn = {".*CallLeaf.*frem.*"}, phase = CompilePhase.BEFORE_MATCHING) public float alsoConstant() { // Make sure value is only available after second loop opts round @@ -102,7 +105,7 @@ public class ModFNodeTests { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_F, "2"}, phase = CompilePhase.PHASEIDEALLOOP1) // Only constant fold after some loop opts - @IR(counts = {".*CallLeaf.*frem.*", "0"}, + @IR(failOn = {".*CallLeaf.*frem.*"}, phase = CompilePhase.BEFORE_MATCHING) public float nanLeftConstant() { // Make sure value is only available after second loop opts round @@ -120,7 +123,7 @@ public class ModFNodeTests { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_F, "2"}, phase = CompilePhase.PHASEIDEALLOOP1) // Only constant fold after some loop opts - @IR(counts = {".*CallLeaf.*frem.*", "0"}, + @IR(failOn = {".*CallLeaf.*frem.*"}, phase = CompilePhase.BEFORE_MATCHING) public float nanRightConstant() { // Make sure value is only available after second loop opts round @@ -154,9 +157,9 @@ public class ModFNodeTests { @Test @IR(counts = {IRNode.MOD_F, "1"}, phase = CompilePhase.AFTER_PARSING) - @IR(counts = {IRNode.MOD_F, "0"}, + @IR(failOn = {IRNode.MOD_F}, phase = CompilePhase.ITER_GVN1) // IGVN removes unused nodes - @IR(counts = {".*CallLeaf.*frem.*", "0"}, + @IR(failOn = {".*CallLeaf.*frem.*"}, phase = CompilePhase.BEFORE_MATCHING) public void unusedResult(float x, float y) { float unused = x % y; @@ -165,9 +168,9 @@ public class ModFNodeTests { @Test @IR(counts = {IRNode.MOD_F, "1"}, phase = CompilePhase.AFTER_PARSING) - @IR(counts = {IRNode.MOD_F, "0"}, + @IR(failOn = {IRNode.MOD_F}, phase = CompilePhase.ITER_GVN1) // IGVN removes unused nodes - @IR(counts = {".*CallLeaf.*frem.*", "0"}, + @IR(failOn = {".*CallLeaf.*frem.*"}, phase = CompilePhase.BEFORE_MATCHING) public void repeatedlyUnused(float x, float y) { float unused = 1.f; @@ -185,9 +188,9 @@ public class ModFNodeTests { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_F, "1"}, phase = CompilePhase.ITER_GVN2) - @IR(counts = {IRNode.MOD_F, "0"}, + @IR(failOn = {IRNode.MOD_F}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) - @IR(counts = {".*CallLeaf.*frem.*", "0"}, + @IR(failOn = {".*CallLeaf.*frem.*"}, phase = CompilePhase.BEFORE_MATCHING) public float unusedResultAfterLoopOpt1(float x, float y) { float unused = x % y; @@ -210,9 +213,9 @@ public class ModFNodeTests { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_F, "1"}, phase = CompilePhase.AFTER_CLOOPS) - @IR(counts = {IRNode.MOD_F, "0"}, + @IR(failOn = {IRNode.MOD_F}, phase = CompilePhase.PHASEIDEALLOOP1) - @IR(counts = {".*CallLeaf.*frem.*", "0"}, + @IR(failOn = {".*CallLeaf.*frem.*"}, phase = CompilePhase.BEFORE_MATCHING) public float unusedResultAfterLoopOpt2(float x, float y) { int a = 77; @@ -235,9 +238,9 @@ public class ModFNodeTests { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_F, "2"}, phase = CompilePhase.AFTER_CLOOPS) // drop the useless one - @IR(counts = {IRNode.MOD_F, "0"}, + @IR(failOn = {IRNode.MOD_F}, phase = CompilePhase.PHASEIDEALLOOP1) // drop the rest - @IR(counts = {".*CallLeaf.*frem.*", "0"}, + @IR(failOn = {".*CallLeaf.*frem.*"}, phase = CompilePhase.BEFORE_MATCHING) public float unusedResultAfterLoopOpt3(float x, float y) { float unused = x % y; @@ -252,4 +255,25 @@ public class ModFNodeTests { int other = (b - 77) * (int)(x % y % 1.f); return (float)other; } + + @Test + @IR(failOn = {IRNode.CMP_F}, + phase = CompilePhase.CCP1) + @IR(failOn = {IRNode.MOD_F}, + phase = CompilePhase.BEFORE_MACRO_EXPANSION) + public float constantFoldInCCP(){ + int i; + for (i = 2; i < 4; i *= 2) { + } + int j; + for (j = 2; j < 4; j *= 2) { + } + volatileField = 42; + float v1 = (float) i / 2; + float v2 = j; + float v = v1 % v2; + for (; v < v2; v *= 2) { + } + return v; + } } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestShiftAndMask.java b/test/hotspot/jtreg/compiler/c2/irTests/TestShiftAndMask.java index 1413ee0cafa..8f798a1b7eb 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestShiftAndMask.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestShiftAndMask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Red Hat, Inc. All rights reserved. + * Copyright (c) 2021, 2026, Red Hat, Inc. 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 @@ -24,13 +24,14 @@ package compiler.c2.irTests; import compiler.lib.ir_framework.*; -import jdk.test.lib.Utils; -import java.util.Random; +import static compiler.lib.generators.Generators.G; +import jdk.test.lib.Asserts; + import java.util.Objects; /* * @test - * @bug 8277850 8278949 8285793 8346664 + * @bug 8277850 8278949 8285793 8346664 8380475 * @key randomness * @summary C2: optimize mask checks in counted loops * @library /test/lib / @@ -38,33 +39,31 @@ import java.util.Objects; */ public class TestShiftAndMask { - private static final Random RANDOM = Utils.getRandomInstance(); - public static void main(String[] args) { TestFramework.run(); } // any X << INT_MASK_WIDTH is zero under any INT_MASK - static final int INT_MASK_WIDTH = 1 + RANDOM.nextInt(30); + static final int INT_MASK_WIDTH = G.ints().restricted(1, 30).next(); static final int INT_MAX_MASK = (1 << INT_MASK_WIDTH) - 1; - static final int INT_MASK = 1 + RANDOM.nextInt(INT_MAX_MASK); - static final int INT_MASK2 = 1 + RANDOM.nextInt(INT_MAX_MASK); - static final int INT_ZERO_CONST = RANDOM.nextInt() << INT_MASK_WIDTH; + static final int INT_MASK = G.ints().restricted(1, INT_MAX_MASK).next(); + static final int INT_MASK2 = G.ints().restricted(1, INT_MAX_MASK).next(); + static final int INT_ZERO_CONST = G.ints().next() << INT_MASK_WIDTH; - static final int INT_RANDOM_CONST = RANDOM.nextInt(); - static final int INT_RANDOM_SHIFT = RANDOM.nextInt(); - static final int INT_RANDOM_MASK = RANDOM.nextInt(); + static final int INT_RANDOM_CONST = G.ints().next(); + static final int INT_RANDOM_SHIFT = G.ints().next(); + static final int INT_RANDOM_MASK = G.ints().next(); // any X << LONG_MASK_WIDTH is zero under any LONG_MASK - static final int LONG_MASK_WIDTH = 1 + RANDOM.nextInt(62); + static final int LONG_MASK_WIDTH = G.ints().restricted(1, 62).next(); static final long LONG_MAX_MASK = (1L << LONG_MASK_WIDTH) - 1; - static final long LONG_MASK = 1 + RANDOM.nextLong(LONG_MAX_MASK); - static final long LONG_MASK2 = 1 + RANDOM.nextLong(LONG_MAX_MASK); - static final long LONG_ZERO_CONST = RANDOM.nextLong() << LONG_MASK_WIDTH; + static final long LONG_MASK = G.longs().restricted(1L, LONG_MAX_MASK).next(); + static final long LONG_MASK2 = G.longs().restricted(1L, LONG_MAX_MASK).next(); + static final long LONG_ZERO_CONST = G.longs().next() << LONG_MASK_WIDTH; - static final long LONG_RANDOM_CONST = RANDOM.nextLong(); - static final long LONG_RANDOM_SHIFT = RANDOM.nextLong(); - static final long LONG_RANDOM_MASK = RANDOM.nextLong(); + static final long LONG_RANDOM_CONST = G.longs().next(); + static final long LONG_RANDOM_SHIFT = G.longs().next(); + static final long LONG_RANDOM_MASK = G.longs().next(); @Test public static int intSumAndMask(int i, int j) { @@ -73,12 +72,9 @@ public class TestShiftAndMask { @Run(test = { "intSumAndMask" }) public static void checkIntSumAndMask() { - int j = RANDOM.nextInt(); - int i = RANDOM.nextInt(); - int res = intSumAndMask(i, j); - if (res != ((j + i << INT_RANDOM_SHIFT + INT_RANDOM_CONST) & INT_RANDOM_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + int j = G.ints().next(); + int i = G.ints().next(); + Asserts.assertEquals(intSumAndMask(i, j), (j + i << INT_RANDOM_SHIFT + INT_RANDOM_CONST) & INT_RANDOM_MASK); } @Test @@ -90,9 +86,7 @@ public class TestShiftAndMask { @Check(test = "shiftMaskInt") public static void checkShiftMaskInt(int res) { - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + Asserts.assertEquals(res, 0); } @Test @@ -102,12 +96,9 @@ public class TestShiftAndMask { @Run(test = { "longSumAndMask" }) public static void checkLongSumAndMask() { - long j = RANDOM.nextLong(); - long i = RANDOM.nextLong(); - long res = longSumAndMask(i, j); - if (res != ((j + i << LONG_RANDOM_SHIFT + LONG_RANDOM_CONST) & LONG_RANDOM_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + long j = G.longs().next(); + long i = G.longs().next(); + Asserts.assertEquals(longSumAndMask(i, j), (j + i << LONG_RANDOM_SHIFT + LONG_RANDOM_CONST) & LONG_RANDOM_MASK); } @Test @@ -120,9 +111,7 @@ public class TestShiftAndMask { @Check(test = "shiftMaskLong") public static void checkShiftMaskLong(long res) { - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + Asserts.assertEquals(res, 0L); } static volatile int barrier; @@ -143,9 +132,7 @@ public class TestShiftAndMask { @Check(test = "shiftNonConstMaskInt") public static void checkShiftNonConstMaskInt(int res) { - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + Asserts.assertEquals(res, 0); } @Test @@ -164,9 +151,7 @@ public class TestShiftAndMask { @Check(test = "shiftNonConstMaskLong") public static void checkShiftNonConstMaskLong(long res) { - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + Asserts.assertEquals(res, 0L); } @Test @@ -178,12 +163,9 @@ public class TestShiftAndMask { @Run(test = "addShiftMaskInt") public static void addShiftMaskInt_runner() { - int i = RANDOM.nextInt(); - int j = RANDOM.nextInt(); - int res = addShiftMaskInt(i, j); - if (res != (j & INT_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.ints().next(); + int j = G.ints().next(); + Asserts.assertEquals(addShiftMaskInt(i, j), j & INT_MASK); } @Test @@ -195,12 +177,9 @@ public class TestShiftAndMask { @Run(test = "addShiftPlusConstMaskInt") public static void addShiftPlusConstMaskInt_runner() { - int i = RANDOM.nextInt(); - int j = RANDOM.nextInt(); - int res = addShiftPlusConstMaskInt(i, j); - if (res != (j & INT_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.ints().next(); + int j = G.ints().next(); + Asserts.assertEquals(addShiftPlusConstMaskInt(i, j), j & INT_MASK); } @Test @@ -233,16 +212,10 @@ public class TestShiftAndMask { @Run(test = "addSshiftNonConstMaskInt") public static void addSshiftNonConstMaskInt_runner() { - int i = RANDOM.nextInt(); - int j = RANDOM.nextInt(); - int res = addSshiftNonConstMaskInt(i, j, true); - if (res != (j & INT_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } - res = addSshiftNonConstMaskInt(i, j, false); - if (res != (j & INT_MASK2)) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.ints().next(); + int j = G.ints().next(); + Asserts.assertEquals(addSshiftNonConstMaskInt(i, j, true), j & INT_MASK); + Asserts.assertEquals(addSshiftNonConstMaskInt(i, j, false), j & INT_MASK2); } @Test @@ -261,15 +234,9 @@ public class TestShiftAndMask { @Run(test = "addConstNonConstMaskInt") public static void addConstNonConstMaskInt_runner() { - int j = RANDOM.nextInt(); - int res = addConstNonConstMaskInt(j, true); - if (res != (j & INT_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } - res = addConstNonConstMaskInt(j, false); - if (res != (j & INT_MASK2)) { - throw new RuntimeException("incorrect result: " + res); - } + int j = G.ints().next(); + Asserts.assertEquals(addConstNonConstMaskInt(j, true), j & INT_MASK); + Asserts.assertEquals(addConstNonConstMaskInt(j, false), j & INT_MASK2); } @Test @@ -281,12 +248,9 @@ public class TestShiftAndMask { @Run(test = "addShiftMaskLong") public static void addShiftMaskLong_runner() { - long i = RANDOM.nextLong(); - long j = RANDOM.nextLong(); - long res = addShiftMaskLong(i, j); - if (res != (j & LONG_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + long i = G.longs().next(); + long j = G.longs().next(); + Asserts.assertEquals(addShiftMaskLong(i, j), j & LONG_MASK); } @Test @@ -298,12 +262,9 @@ public class TestShiftAndMask { @Run(test = "addShiftPlusConstMaskLong") public static void addShiftPlusConstMaskLong_runner() { - long i = RANDOM.nextLong(); - long j = RANDOM.nextLong(); - long res = addShiftPlusConstMaskLong(i, j); - if (res != (j & LONG_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + long i = G.longs().next(); + long j = G.longs().next(); + Asserts.assertEquals(addShiftPlusConstMaskLong(i, j), j & LONG_MASK); } @Test @@ -322,16 +283,10 @@ public class TestShiftAndMask { @Run(test = "addSshiftNonConstMaskLong") public static void addSshiftNonConstMaskLong_runner() { - long i = RANDOM.nextLong(); - long j = RANDOM.nextLong(); - long res = addSshiftNonConstMaskLong(i, j, true); - if (res != (j & LONG_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } - res = addSshiftNonConstMaskLong(i, j, false); - if (res != (j & LONG_MASK2)) { - throw new RuntimeException("incorrect result: " + res); - } + long i = G.longs().next(); + long j = G.longs().next(); + Asserts.assertEquals(addSshiftNonConstMaskLong(i, j, true), j & LONG_MASK); + Asserts.assertEquals(addSshiftNonConstMaskLong(i, j, false), j & LONG_MASK2); } @Test @@ -350,15 +305,9 @@ public class TestShiftAndMask { @Run(test = "addConstNonConstMaskLong") public static void addConstNonConstMaskLong_runner() { - long j = RANDOM.nextLong(); - long res = addConstNonConstMaskLong(j, true); - if (res != (j & LONG_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } - res = addConstNonConstMaskLong(j, false); - if (res != (j & LONG_MASK2)) { - throw new RuntimeException("incorrect result: " + res); - } + long j = G.longs().next(); + Asserts.assertEquals(addConstNonConstMaskLong(j, true), j & LONG_MASK); + Asserts.assertEquals(addConstNonConstMaskLong(j, false), j & LONG_MASK2); } @Test @@ -370,9 +319,7 @@ public class TestShiftAndMask { @Check(test = "addShiftMaskInt2") public static void checkAddShiftMaskInt2(int res) { - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + Asserts.assertEquals(res, 0); } @Test @@ -384,9 +331,7 @@ public class TestShiftAndMask { @Check(test = "addShiftMaskLong2") public static void checkAddShiftMaskLong2(long res) { - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + Asserts.assertEquals(res, 0L); } // Try to get add inputs swapped compared to other tests @@ -401,12 +346,9 @@ public class TestShiftAndMask { @Run(test = "addShiftMaskInt3") public static void addShiftMaskInt3_runner() { - int i = RANDOM.nextInt(); - int j = RANDOM.nextInt(); - int res = addShiftMaskInt3(i, j); - if (res != (j & INT_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.ints().next(); + int j = G.ints().next(); + Asserts.assertEquals(addShiftMaskInt3(i, j), j & INT_MASK); } @Test @@ -420,12 +362,9 @@ public class TestShiftAndMask { @Run(test = "addShiftMaskLong3") public static void addShiftMaskLong3_runner() { - long i = RANDOM.nextLong(); - float j = RANDOM.nextFloat(); - long res = addShiftMaskLong3(i, j); - if (res != (((long) j) & LONG_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + long i = G.longs().next(); + float j = G.floats().next(); + Asserts.assertEquals(addShiftMaskLong3(i, j), ((long) j) & LONG_MASK); } @Test @@ -437,9 +376,7 @@ public class TestShiftAndMask { @Check(test = "shiftConvMask") public static void checkShiftConvMask(long res) { - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + Asserts.assertEquals(res, 0L); } @Test @@ -458,9 +395,7 @@ public class TestShiftAndMask { @Check(test = "shiftNotConstConvMask") public static void checkShiftNotConstConvMask(long res) { - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + Asserts.assertEquals(res, 0L); } @Test @@ -472,12 +407,9 @@ public class TestShiftAndMask { @Run(test = "addShiftConvMask") public static void addShiftConvMask_runner() { - int i = RANDOM.nextInt(); - long j = RANDOM.nextLong(); - long res = addShiftConvMask(i, j); - if (res != (j & INT_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.ints().next(); + long j = G.longs().next(); + Asserts.assertEquals(addShiftConvMask(i, j), j & INT_MASK); } @Test @@ -489,9 +421,7 @@ public class TestShiftAndMask { @Check(test = "addShiftConvMask2") public static void checkAddShiftConvMask2(long res) { - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + Asserts.assertEquals(res, 0L); } @Test @@ -502,11 +432,8 @@ public class TestShiftAndMask { @Run(test = "shiftMaskIntCheckIndex") public static void shiftMaskIntCheckIndex_runner() { - int i = RANDOM.nextInt((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH); - int res = shiftMaskIntCheckIndex(i, (i << INT_MASK_WIDTH) + 1); - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.uniformInts(0, ((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH) - 1).next(); + Asserts.assertEquals(shiftMaskIntCheckIndex(i, (i << INT_MASK_WIDTH) + 1), 0); } @Test @@ -517,11 +444,8 @@ public class TestShiftAndMask { @Run(test = "shiftMaskLongCheckIndex") public static void shiftMaskLongCheckIndex_runner() { - long i = RANDOM.nextLong((Long.MAX_VALUE - 1) >> LONG_MASK_WIDTH); - long res = shiftMaskLongCheckIndex(i, (i << LONG_MASK_WIDTH) + 1); - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + long i = G.uniformLongs(0L, ((Long.MAX_VALUE - 1) >> LONG_MASK_WIDTH) - 1).next(); + Asserts.assertEquals(shiftMaskLongCheckIndex(i, (i << LONG_MASK_WIDTH) + 1), 0L); } @Test @@ -533,12 +457,9 @@ public class TestShiftAndMask { @Run(test = "addShiftMaskIntCheckIndex") public static void addShiftMaskIntCheckIndex_runner() { - int i = RANDOM.nextInt((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH); - int j = RANDOM.nextInt(); - int res = addShiftMaskIntCheckIndex(i, j, (i << INT_MASK_WIDTH) + 1); - if (res != (j & INT_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.uniformInts(0, ((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH) - 1).next(); + int j = G.ints().next(); + Asserts.assertEquals(addShiftMaskIntCheckIndex(i, j, (i << INT_MASK_WIDTH) + 1), j & INT_MASK); } @Test @@ -550,12 +471,9 @@ public class TestShiftAndMask { @Run(test = "addShiftMaskLongCheckIndex") public static void addShiftMaskLongCheckIndex_runner() { - long i = RANDOM.nextLong((Long.MAX_VALUE - 1) >> LONG_MASK_WIDTH); - long j = RANDOM.nextLong(); - long res = addShiftMaskLongCheckIndex(i, j, (i << LONG_MASK_WIDTH) + 1); - if (res != (j & LONG_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + long i = G.uniformLongs(0L, ((Long.MAX_VALUE - 1) >> LONG_MASK_WIDTH) - 1).next(); + long j = G.longs().next(); + Asserts.assertEquals(addShiftMaskLongCheckIndex(i, j, (i << LONG_MASK_WIDTH) + 1), j & LONG_MASK); } @Test @@ -567,12 +485,9 @@ public class TestShiftAndMask { @Run(test = "addShiftMaskIntCheckIndex2") public static void addShiftMaskIntCheckIndex2_runner() { - int i = RANDOM.nextInt((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH); - int j = RANDOM.nextInt((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH); - int res = addShiftMaskIntCheckIndex2(i, j, (Integer.max(i, j) << INT_MASK_WIDTH) + 1); - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.uniformInts(0, ((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH) - 1).next(); + int j = G.uniformInts(0, ((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH) - 1).next(); + Asserts.assertEquals(addShiftMaskIntCheckIndex2(i, j, (Integer.max(i, j) << INT_MASK_WIDTH) + 1), 0); } @Test @@ -583,12 +498,9 @@ public class TestShiftAndMask { @Run(test = "addShiftMaskLongCheckIndex2") public static void addShiftMaskLongCheckIndex2_runner() { - long i = RANDOM.nextLong((Long.MAX_VALUE - 1) >> LONG_MASK_WIDTH); - long j = RANDOM.nextLong((Long.MAX_VALUE - 1) >> LONG_MASK_WIDTH); - long res = addShiftMaskLongCheckIndex2(i, j, (Long.max(i, j) << LONG_MASK_WIDTH) + 1); - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + long i = G.uniformLongs(0L, ((Long.MAX_VALUE - 1) >> LONG_MASK_WIDTH) - 1).next(); + long j = G.uniformLongs(0L, ((Long.MAX_VALUE - 1) >> LONG_MASK_WIDTH) - 1).next(); + Asserts.assertEquals(addShiftMaskLongCheckIndex2(i, j, (Long.max(i, j) << LONG_MASK_WIDTH) + 1), 0L); } @Test @@ -599,11 +511,8 @@ public class TestShiftAndMask { @Run(test = "shiftConvMaskCheckIndex") public static void shiftConvMaskCheckIndex_runner() { - int i = RANDOM.nextInt((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH); - long res = shiftConvMaskCheckIndex(i, (i << INT_MASK_WIDTH) + 1); - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.uniformInts(0, ((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH) - 1).next(); + Asserts.assertEquals(shiftConvMaskCheckIndex(i, (i << INT_MASK_WIDTH) + 1), 0L); } @Test @@ -615,12 +524,9 @@ public class TestShiftAndMask { @Run(test = "addShiftConvMaskCheckIndex") public static void addShiftConvMaskCheckIndex_runner() { - int i = RANDOM.nextInt((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH); - long j = RANDOM.nextLong(); - long res = addShiftConvMaskCheckIndex(i, j, (i << INT_MASK_WIDTH) + 1); - if (res != (j & INT_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.uniformInts(0, ((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH) - 1).next(); + long j = G.longs().next(); + Asserts.assertEquals(addShiftConvMaskCheckIndex(i, j, (i << INT_MASK_WIDTH) + 1), j & INT_MASK); } @Test @@ -631,11 +537,147 @@ public class TestShiftAndMask { @Run(test = "addShiftConvMaskCheckIndex2") public static void addShiftConvMaskCheckIndex2_runner() { - int i = RANDOM.nextInt((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH); - int j = RANDOM.nextInt((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH); - long res = addShiftConvMaskCheckIndex2(i, j, (Integer.max(i, j) << INT_MASK_WIDTH) + 1); - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.uniformInts(0, ((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH) - 1).next(); + int j = G.uniformInts(0, ((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH) - 1).next(); + Asserts.assertEquals(addShiftConvMaskCheckIndex2(i, j, (Integer.max(i, j) << INT_MASK_WIDTH) + 1), 0L); + } + + @Test + @IR(failOn = {IRNode.AND_I}) + @IR(counts = {IRNode.LSHIFT_I, "1"}) + public int shiftLeftWithLowMaskInt(int x) { + return (x << INT_MASK_WIDTH) & (-1 << INT_MASK_WIDTH); // transformed to: return x << INT_MASK_WIDTH; + } + + @Test + @IR(failOn = {IRNode.AND_L}) + @IR(counts = {IRNode.LSHIFT_L, "1"}) + public long shiftLeftWithLowMaskLong(long x) { + return (x << LONG_MASK_WIDTH) & (-1 << LONG_MASK_WIDTH); // transformed to: return x << LONG_MASK_WIDTH; + } + + @Test + @IR(failOn = {IRNode.AND_I}) + @IR(counts = {IRNode.LSHIFT_I, "1"}) + public int shiftLeftWithLowMaskSmallInt(int x) { + return (x << INT_MASK_WIDTH) & (-1 << (INT_MASK_WIDTH - 1)); // transformed to: return x << INT_MASK_WIDTH; + } + + @Test + @IR(failOn = {IRNode.AND_L}) + @IR(counts = {IRNode.LSHIFT_L, "1"}) + public long shiftLeftWithLowMaskSmallLong(long x) { + return (x << LONG_MASK_WIDTH) & (-1 << (LONG_MASK_WIDTH - 1)); // transformed to: return x << LONG_MASK_WIDTH; + } + + @Test + @IR(failOn = {IRNode.AND_I}) + @IR(counts = {IRNode.LSHIFT_I, "1"}) + public static int shiftLeftWithLowMaskIntReversed(int x) { + return (-1 << INT_MASK_WIDTH) & (x << INT_MASK_WIDTH); // transformed to: return x << INT_MASK_WIDTH; + } + + @Test + @IR(failOn = {IRNode.AND_L}) + @IR(counts = {IRNode.LSHIFT_L, "1"}) + public static long shiftLeftWithLowMaskLongReversed(long x) { + return (-1 << LONG_MASK_WIDTH) & (x << LONG_MASK_WIDTH); // transformed to: return x << LONG_MASK_WIDTH; + } + + @Test + @IR(failOn = {IRNode.AND_I}) + @IR(counts = {IRNode.LSHIFT_I, "1"}) + public static int shiftLeftWithLowMaskSmallIntReversed(int x) { + return (-1 << (INT_MASK_WIDTH - 1)) & (x << INT_MASK_WIDTH); // transformed to: return x << INT_MASK_WIDTH; + } + + @Test + @IR(failOn = {IRNode.AND_L}) + @IR(counts = {IRNode.LSHIFT_L, "1"}) + public static long shiftLeftWithLowMaskSmallLongReversed(long x) { + return (-1 << (LONG_MASK_WIDTH - 1)) & (x << LONG_MASK_WIDTH); // transformed to: return x << LONG_MASK_WIDTH; + } + + @Test + @IR(counts = {IRNode.AND_I, "1"}) + public int andMaskNonNegativeInt(int x) { + return (x & 0x7FFF) & 0xFFFF; // transformed to: return x & 0x7FFF; + } + + @Test + @IR(counts = {IRNode.AND_L, "1"}) + public long andMaskNonNegativeLong(long x) { + return (x & 0x7FFFL) & 0xFFFFL; // transformed to: return x & 0x7FFFL; + } + + @Test + @IR(counts = {IRNode.AND_I, "1"}) + public int andMaskNonNegativeIntReversed(int x) { + return 0xFFFF & (x & 0x7FFF); // transformed to: return x & 0x7FFF; + } + + @Test + @IR(counts = {IRNode.AND_L, "1"}) + public long andMaskNonNegativeLongReversed(long x) { + return 0xFFFFL & (x & 0x7FFFL); // transformed to: return x & 0x7FFFL; + } + + @Test + @IR(failOn = {IRNode.AND_I}) + @IR(counts = {IRNode.URSHIFT_I, "1"}) + public int andAfterURShiftInt(int x) { + return (x >>> 8) & 0x00FFFFFF; // transformed to return x >>> 8; + } + + @Test + @IR(failOn = {IRNode.AND_L}) + @IR(counts = {IRNode.URSHIFT_L, "1"}) + public long andAfterURShiftLong(long x) { + return (x >>> 16) & 0x0000FFFFFFFFFFFFL; // transformed to return x >>> 16; + } + + @Test + @IR(failOn = {IRNode.AND_I}) + @IR(counts = {IRNode.URSHIFT_I, "1"}) + public int andAfterURShiftIntReversed(int x) { + return 0x00FFFFFF & (x >>> 8); // transformed to return x >>> 8; + } + + @Test + @IR(failOn = {IRNode.AND_L}) + @IR(counts = {IRNode.URSHIFT_L, "1"}) + public long andAfterURShiftLongReversed(long x) { + return 0x0000FFFFFFFFFFFFL & (x >>> 16); // transformed to return x >>> 16; + } + + @Run(test = {"shiftLeftWithLowMaskInt", "shiftLeftWithLowMaskLong", + "shiftLeftWithLowMaskSmallInt", "shiftLeftWithLowMaskSmallLong", + "shiftLeftWithLowMaskIntReversed", "shiftLeftWithLowMaskLongReversed", + "shiftLeftWithLowMaskSmallIntReversed", "shiftLeftWithLowMaskSmallLongReversed", + "andMaskNonNegativeInt", "andMaskNonNegativeLong", + "andMaskNonNegativeIntReversed", "andMaskNonNegativeLongReversed", + "andAfterURShiftInt", "andAfterURShiftLong", + "andAfterURShiftIntReversed", "andAfterURShiftLongReversed", + }) + public void verifyShiftAndMaskTransforms() { + int xi = G.ints().next(); + long xl = G.longs().next(); + + Asserts.assertEquals(shiftLeftWithLowMaskInt(xi), (xi << INT_MASK_WIDTH)); + Asserts.assertEquals(shiftLeftWithLowMaskLong(xl), (xl << LONG_MASK_WIDTH)); + Asserts.assertEquals(shiftLeftWithLowMaskSmallInt(xi), (xi << INT_MASK_WIDTH)); + Asserts.assertEquals(shiftLeftWithLowMaskSmallLong(xl), (xl << LONG_MASK_WIDTH)); + Asserts.assertEquals(shiftLeftWithLowMaskIntReversed(xi), (xi << INT_MASK_WIDTH)); + Asserts.assertEquals(shiftLeftWithLowMaskLongReversed(xl), (xl << LONG_MASK_WIDTH)); + Asserts.assertEquals(shiftLeftWithLowMaskSmallIntReversed(xi), (xi << INT_MASK_WIDTH)); + Asserts.assertEquals(shiftLeftWithLowMaskSmallLongReversed(xl), (xl << LONG_MASK_WIDTH)); + Asserts.assertEquals(andMaskNonNegativeInt(xi), (xi & 0x7FFF)); + Asserts.assertEquals(andMaskNonNegativeLong(xl), (xl & 0x7FFFL)); + Asserts.assertEquals(andMaskNonNegativeIntReversed(xi), (xi & 0x7FFF)); + Asserts.assertEquals(andMaskNonNegativeLongReversed(xl), (xl & 0x7FFFL)); + Asserts.assertEquals(andAfterURShiftInt(xi), (xi >>> 8)); + Asserts.assertEquals(andAfterURShiftLong(xl), (xl >>> 16)); + Asserts.assertEquals(andAfterURShiftIntReversed(xi), (xi >>> 8)); + Asserts.assertEquals(andAfterURShiftLongReversed(xl), (xl >>> 16)); } } diff --git a/test/hotspot/jtreg/compiler/codecache/stress/OverloadCompileQueueTest.java b/test/hotspot/jtreg/compiler/codecache/stress/OverloadCompileQueueTest.java index 548a746c3af..7122a81c1e7 100644 --- a/test/hotspot/jtreg/compiler/codecache/stress/OverloadCompileQueueTest.java +++ b/test/hotspot/jtreg/compiler/codecache/stress/OverloadCompileQueueTest.java @@ -112,9 +112,9 @@ public class OverloadCompileQueueTest implements Runnable { AVAILABLE_LEVELS = IntStream .rangeClosed(LEVEL_SIMPLE, TIERED_STOP_AT_LEVEL) .toArray(); - } else if (Platform.isServer() && !Platform.isEmulatedClient()) { + } else if (Platform.isServer()) { AVAILABLE_LEVELS = new int[] { LEVEL_FULL_OPTIMIZATION }; - } else if (Platform.isClient() || Platform.isMinimal() || Platform.isEmulatedClient()) { + } else if (Platform.isClient() || Platform.isMinimal()) { AVAILABLE_LEVELS = new int[] { LEVEL_SIMPLE }; } else { throw new Error("TESTBUG: unknown VM: " + Platform.vmName); diff --git a/test/hotspot/jtreg/compiler/codegen/TestCheckCastPPRawOopSchedulingAtSafepoint.java b/test/hotspot/jtreg/compiler/codegen/TestCheckCastPPRawOopSchedulingAtSafepoint.java new file mode 100644 index 00000000000..00ba4be76f8 --- /dev/null +++ b/test/hotspot/jtreg/compiler/codegen/TestCheckCastPPRawOopSchedulingAtSafepoint.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2026, 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 8372649 8376189 + * @summary CheckCastPP with RawPtr input can be scheduled below a safepoint during C2 + * post-regalloc block-local scheduling, causing a live raw oop at the safepoint + * instead of a corresponding oop in the OopMap. + * @library /test/lib + * @modules jdk.incubator.vector + * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:TrackedInitializationLimit=0 + * -XX:CompileCommand=compileonly,${test.main.class}::test8372649 + * -XX:+OptoScheduling ${test.main.class} 8372649 + * @run main/othervm --add-modules=jdk.incubator.vector + * -XX:+OptoScheduling ${test.main.class} 8376189 + */ + +package compiler.codegen; + +import jdk.incubator.vector.*; +import jdk.test.lib.Asserts; + +public class TestCheckCastPPRawOopSchedulingAtSafepoint { + private static final VectorSpecies SPECIES_I = IntVector.SPECIES_64; + + static public void main(String[] args) { + String mode = args[0]; + if ("8372649".equals(mode)) { + run8372649(); + } else if ("8376189".equals(mode)) { + run8376189(); + } + } + + // JDK-8372649 + static void run8372649(){ + for (int j = 6; 116 > j; ++j) { + test8372649(); + } + Asserts.assertEQ(Test.c, 43560L); + } + + static class Test { + static int a = 256; + float[] b = new float[256]; + static long c; + } + + static void test8372649() { + float[][] g = new float[Test.a][Test.a]; + for (int d = 7; d < 16; d++) { + long e = 1; + do { + g[d][(int) e] = d; + synchronized (new Test()) {} + } while (++e < 5); + } + for (int i = 0; i < Test.a; ++i) { + for (int j = 0; j < Test.a ; ++j) { + Test.c += g[i][j]; + } + } + } + + // JDK-8376189 + static void run8376189(){ + int[] a = new int[10_000]; + int r = 0; + for (int i = 0; i < 10_000; i++) { + r = test8376189(a); + } + Asserts.assertEQ(r, 0); + } + + public static int test8376189(int[] a) { + var mins = IntVector.broadcast(SPECIES_I, a[0]); + for (int i = 0; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + mins = IntVector.fromArray(SPECIES_I, a, 0); + } + return mins.reduceLanes(VectorOperators.MIN); + } +} \ No newline at end of file diff --git a/test/hotspot/jtreg/compiler/cpuflags/TestAESIntrinsicsOnSupportedConfig.java b/test/hotspot/jtreg/compiler/cpuflags/TestAESIntrinsicsOnSupportedConfig.java index 59c8b2efa11..421307e85ca 100644 --- a/test/hotspot/jtreg/compiler/cpuflags/TestAESIntrinsicsOnSupportedConfig.java +++ b/test/hotspot/jtreg/compiler/cpuflags/TestAESIntrinsicsOnSupportedConfig.java @@ -76,7 +76,7 @@ public class TestAESIntrinsicsOnSupportedConfig extends AESIntrinsicsBase { prepareArguments(prepareBooleanFlag(AESIntrinsicsBase .USE_AES, true))); final String errorMessage = "Case testUseAES failed"; - if (Platform.isServer() && !Platform.isEmulatedClient() && isTieredLevelGreaterThan(3)) { + if (Platform.isServer() && isTieredLevelGreaterThan(3)) { verifyOutput(new String[]{AESIntrinsicsBase.CIPHER_INTRINSIC, AESIntrinsicsBase.AES_INTRINSIC}, null, errorMessage, outputAnalyzer); diff --git a/test/hotspot/jtreg/compiler/gcbarriers/TestZGCBarrierElision.java b/test/hotspot/jtreg/compiler/gcbarriers/TestZGCBarrierElision.java index ff20f750935..38aaee62045 100644 --- a/test/hotspot/jtreg/compiler/gcbarriers/TestZGCBarrierElision.java +++ b/test/hotspot/jtreg/compiler/gcbarriers/TestZGCBarrierElision.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -251,7 +251,8 @@ class TestZGCCorrectBarrierElision { class TestZGCEffectiveBarrierElision { @Test - @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) + // C2 does not emit a field load during parsing, so it also does not emit any barriers. + @IR(failOn = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED }, phase = CompilePhase.FINAL_CODE) static void testAllocateThenLoad() { Outer o1 = new Outer(); Common.blackhole(o1); diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index 875ef57c865..33be24a0367 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -52,6 +52,7 @@ import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.$; import compiler.lib.template_framework.library.CodeGenerationDataNameType; import compiler.lib.template_framework.library.Expression; +import compiler.lib.template_framework.library.Expression.Nesting; import compiler.lib.template_framework.library.Operations; import compiler.lib.template_framework.library.PrimitiveType; import compiler.lib.template_framework.library.TestFrameworkClass; @@ -335,7 +336,7 @@ public class ExpressionFuzzer { for (int i = 0; i < 10; i++) { // The depth determines roughly how many operations are going to be used in the expression. int depth = RANDOM.nextInt(1, 20); - Expression expression = Expression.nestRandomly(type, Operations.PRIMITIVE_OPERATIONS, depth); + Expression expression = Expression.nestRandomly(type, Operations.PRIMITIVE_OPERATIONS, depth, Nesting.EXACT); tests.add(testTemplate.asToken(expression)); } } @@ -350,7 +351,7 @@ public class ExpressionFuzzer { for (int i = 0; i < 2; i++) { // The depth determines roughly how many operations are going to be used in the expression. int depth = RANDOM.nextInt(1, 20); - Expression expression = Expression.nestRandomly(type, Operations.SCALAR_NUMERIC_OPERATIONS, depth); + Expression expression = Expression.nestRandomly(type, Operations.SCALAR_NUMERIC_OPERATIONS, depth, Nesting.EXACT); tests.add(testTemplate.asToken(expression)); } } diff --git a/test/hotspot/jtreg/compiler/inlining/InlineBimorphicVirtualCallAfterMorphismChanged.java b/test/hotspot/jtreg/compiler/inlining/InlineBimorphicVirtualCallAfterMorphismChanged.java index 6f1dadd6b80..bebb3802eb9 100644 --- a/test/hotspot/jtreg/compiler/inlining/InlineBimorphicVirtualCallAfterMorphismChanged.java +++ b/test/hotspot/jtreg/compiler/inlining/InlineBimorphicVirtualCallAfterMorphismChanged.java @@ -29,7 +29,7 @@ * @modules java.base/jdk.internal.misc * @library /test/lib * @requires vm.flagless - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * * @run driver compiler.inlining.InlineBimorphicVirtualCallAfterMorphismChanged */ diff --git a/test/hotspot/jtreg/compiler/intrinsics/IntrinsicAvailableTest.java b/test/hotspot/jtreg/compiler/intrinsics/IntrinsicAvailableTest.java index abf0aa7b885..310fce78f9b 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/IntrinsicAvailableTest.java +++ b/test/hotspot/jtreg/compiler/intrinsics/IntrinsicAvailableTest.java @@ -142,7 +142,7 @@ public class IntrinsicAvailableTest extends CompilerWhiteBoxTest { public void test() throws Exception { Executable intrinsicMethod = testCase.getExecutable(); - if (Platform.isServer() && !Platform.isEmulatedClient() && (TIERED_STOP_AT_LEVEL == COMP_LEVEL_FULL_OPTIMIZATION)) { + if (Platform.isServer() && (TIERED_STOP_AT_LEVEL == COMP_LEVEL_FULL_OPTIMIZATION)) { if (TIERED_COMPILATION) { checkIntrinsicForCompilationLevel(intrinsicMethod, COMP_LEVEL_SIMPLE); } diff --git a/test/hotspot/jtreg/compiler/intrinsics/IntrinsicDisabledTest.java b/test/hotspot/jtreg/compiler/intrinsics/IntrinsicDisabledTest.java index a98bef68c9a..7eb1c855f4b 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/IntrinsicDisabledTest.java +++ b/test/hotspot/jtreg/compiler/intrinsics/IntrinsicDisabledTest.java @@ -217,8 +217,7 @@ public class IntrinsicDisabledTest { } public static void main(String args[]) { - if (Platform.isServer() && !Platform.isEmulatedClient() && - (TIERED_STOP_AT_LEVEL == CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION)) { + if (Platform.isServer() && (TIERED_STOP_AT_LEVEL == CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION)) { if (TIERED_COMPILATION) { test(CompilerWhiteBoxTest.COMP_LEVEL_SIMPLE); } diff --git a/test/hotspot/jtreg/compiler/intrinsics/bigInteger/MontgomeryMultiplyTest.java b/test/hotspot/jtreg/compiler/intrinsics/bigInteger/MontgomeryMultiplyTest.java index e145a0088c0..0ca419f7dcd 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bigInteger/MontgomeryMultiplyTest.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bigInteger/MontgomeryMultiplyTest.java @@ -26,7 +26,7 @@ * @test * @bug 8130150 8131779 8139907 * @summary Verify that the Montgomery multiply and square intrinsic works and correctly checks their arguments. - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @modules java.base/jdk.internal.misc:open * @modules java.base/java.math:open * @library /test/lib / @@ -313,8 +313,8 @@ public class MontgomeryMultiplyTest { } public static void main(String args[]) { - if (!Platform.isServer() || Platform.isEmulatedClient()) { - throw new Error("TESTBUG: Not server mode"); + if (!Platform.isServer()) { + throw new Error("TESTBUG: Not server VM"); } if (wb.isIntrinsicAvailable(getExecutable(true), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) && wb.isIntrinsicAvailable(getExecutable(false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION)) { diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/AndnTestI.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/AndnTestI.java index 4145b0f5641..30ce3b36af6 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/AndnTestI.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/AndnTestI.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/AndnTestL.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/AndnTestL.java index c17e5345d21..1a3e7e1314d 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/AndnTestL.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/AndnTestL.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsiTestI.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsiTestI.java index 881ed37906e..71d9e52a539 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsiTestI.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsiTestI.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsiTestL.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsiTestL.java index 1ea8125b1cd..425f70f1052 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsiTestL.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsiTestL.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsmskTestI.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsmskTestI.java index 38753762f39..3632d9617b8 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsmskTestI.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsmskTestI.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsmskTestL.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsmskTestL.java index 9a279083059..8f6958d212f 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsmskTestL.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsmskTestL.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsrTestI.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsrTestI.java index 4ea2b3ab841..bd8c26724a1 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsrTestI.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsrTestI.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsrTestL.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsrTestL.java index 421e24d07ab..68f82a99bd3 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsrTestL.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsrTestL.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BmiIntrinsicBase.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BmiIntrinsicBase.java index 0e8c8fe9514..8c6120388a1 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BmiIntrinsicBase.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BmiIntrinsicBase.java @@ -83,7 +83,7 @@ public class BmiIntrinsicBase extends CompilerWhiteBoxTest { System.out.println(testCase.name()); - if (TIERED_COMPILATION && TIERED_STOP_AT_LEVEL != CompilerWhiteBoxTest.COMP_LEVEL_MAX || Platform.isEmulatedClient()) { + if (TIERED_COMPILATION && TIERED_STOP_AT_LEVEL != CompilerWhiteBoxTest.COMP_LEVEL_MAX) { System.out.println("TieredStopAtLevel value (" + TIERED_STOP_AT_LEVEL + ") is too low, test SKIPPED"); return; } diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BzhiTestI2L.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BzhiTestI2L.java index 4cf94a0eb8b..872fefd883c 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BzhiTestI2L.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BzhiTestI2L.java @@ -23,7 +23,7 @@ /* * @test - * @requires vm.simpleArch == "x64" & vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.simpleArch == "x64" & vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/LZcntTestI.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/LZcntTestI.java index c905fca34a2..34f1eb4f3a3 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/LZcntTestI.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/LZcntTestI.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/LZcntTestL.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/LZcntTestL.java index cf49937936f..30cadaf200e 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/LZcntTestL.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/LZcntTestL.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/TZcntTestI.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/TZcntTestI.java index 8a8ce4508fa..bc6c1276450 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/TZcntTestI.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/TZcntTestI.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/TZcntTestL.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/TZcntTestL.java index 926f071074e..b5db7b861c3 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/TZcntTestL.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/TZcntTestL.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/klass/CastNullCheckDroppingsTest.java b/test/hotspot/jtreg/compiler/intrinsics/klass/CastNullCheckDroppingsTest.java index 1828e0ba837..31449eefb33 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/klass/CastNullCheckDroppingsTest.java +++ b/test/hotspot/jtreg/compiler/intrinsics/klass/CastNullCheckDroppingsTest.java @@ -26,7 +26,7 @@ * @bug 8054492 * @summary Casting can result in redundant null checks in generated code * @requires vm.hasJFR - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib * @modules java.base/jdk.internal.misc * java.management @@ -93,8 +93,8 @@ public class CastNullCheckDroppingsTest { int[] asink; public static void main(String[] args) throws Exception { - if (!Platform.isServer() || Platform.isEmulatedClient()) { - throw new Error("TESTBUG: Not server mode"); + if (!Platform.isServer()) { + throw new Error("TESTBUG: Not server VM"); } // Make sure background compilation is disabled if (WHITE_BOX.getBooleanVMFlag("BackgroundCompilation")) { diff --git a/test/hotspot/jtreg/compiler/intrinsics/mathexact/sanity/IntrinsicBase.java b/test/hotspot/jtreg/compiler/intrinsics/mathexact/sanity/IntrinsicBase.java index f9b2e156f0c..10ae924c794 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/mathexact/sanity/IntrinsicBase.java +++ b/test/hotspot/jtreg/compiler/intrinsics/mathexact/sanity/IntrinsicBase.java @@ -50,7 +50,7 @@ public abstract class IntrinsicBase extends CompilerWhiteBoxTest { int expectedIntrinsicCount = 0; - if (Platform.isServer() && !Platform.isEmulatedClient()) { + if (Platform.isServer()) { if (TIERED_COMPILATION) { int max_level = TIERED_STOP_AT_LEVEL; expectedIntrinsicCount = (max_level == COMP_LEVEL_MAX) ? 1 : 0; diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsicRangeChecks.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsicRangeChecks.java index a7d2cfe7fa7..ec99bba19d2 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsicRangeChecks.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsicRangeChecks.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -89,12 +89,16 @@ public class TestStringIntrinsicRangeChecks { for (int srcOff = 0; srcOff < SIZE; ++srcOff) { for (int dstOff = 0; dstOff < SIZE; ++dstOff) { for (int len = 0; len < SIZE; ++len) { + int srcEnd = srcOff + len; + int dstEnd = dstOff + len; // Check for potential overlows in source or destination array boolean srcOverflow = (srcOff + len) > SIZE; boolean srcOverflowB = (2*srcOff + 2*len) > SIZE; boolean dstOverflow = (dstOff + len) > SIZE; boolean dstOverflowB = (2*dstOff + 2*len) > SIZE; - boolean getCharsOver = (srcOff < len) && ((2*(len-1) >= SIZE) || ((dstOff + len - srcOff) > SIZE)); + boolean getCharsOver = srcOff > srcEnd || (2*srcEnd) > SIZE || // src + (2*len) > SIZE || // len + dstOff > dstEnd || dstEnd > SIZE; // dst // Check if an exception is thrown and bail out if result is inconsistent with above // assumptions (for example, an exception was not thrown although an overflow happened). check(compressByte, srcOverflowB || dstOverflow, byteArray, srcOff, SIZE, dstOff, len); @@ -102,7 +106,7 @@ public class TestStringIntrinsicRangeChecks { check(inflateByte, srcOverflow || dstOverflowB, byteArray, srcOff, SIZE, dstOff, len); check(inflateChar, srcOverflow || dstOverflow, byteArray, srcOff, SIZE, dstOff, len); check(toBytes, srcOverflow, charArray, srcOff, len); - check(getChars, getCharsOver, byteArray, srcOff, len, SIZE, dstOff); + check(getChars, getCharsOver, byteArray, srcOff, srcEnd, SIZE, dstOff); } } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 3508c06ad0a..ebc95527344 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -24,7 +24,7 @@ package compiler.lib.ir_framework; import compiler.lib.ir_framework.driver.irmatching.mapping.*; -import compiler.lib.ir_framework.driver.irmatching.parser.VMInfo; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import compiler.lib.ir_framework.shared.CheckedTestFrameworkException; import compiler.lib.ir_framework.shared.TestFormat; import compiler.lib.ir_framework.shared.TestFormatException; @@ -117,13 +117,14 @@ public class IRNode { public static final String VECTOR_SIZE_32 = VECTOR_SIZE + "32"; public static final String VECTOR_SIZE_64 = VECTOR_SIZE + "64"; - private static final String TYPE_BYTE = "B"; - private static final String TYPE_CHAR = "C"; - private static final String TYPE_SHORT = "S"; - private static final String TYPE_INT = "I"; - private static final String TYPE_LONG = "J"; - private static final String TYPE_FLOAT = "F"; - private static final String TYPE_DOUBLE = "D"; + private static final String TYPE_BYTE = "B"; + private static final String TYPE_CHAR = "C"; + private static final String TYPE_SHORT = "S"; + private static final String TYPE_INT = "I"; + private static final String TYPE_LONG = "J"; + private static final String TYPE_FLOAT = "F"; + private static final String TYPE_DOUBLE = "D"; + private static final String TYPE_BOOLEAN = "Z"; /** * IR placeholder string to regex-for-compile-phase map. @@ -1110,6 +1111,11 @@ public class IRNode { vectorNode(LOAD_VECTOR_D, "LoadVector", TYPE_DOUBLE); } + public static final String LOAD_VECTOR_Z = VECTOR_PREFIX + "LOAD_VECTOR_Z" + POSTFIX; + static { + vectorNode(LOAD_VECTOR_Z, "LoadVector", TYPE_BOOLEAN); + } + public static final String LOAD_VECTOR_GATHER = PREFIX + "LOAD_VECTOR_GATHER" + POSTFIX; static { beforeMatchingNameRegex(LOAD_VECTOR_GATHER, "LoadVectorGather"); @@ -3512,11 +3518,11 @@ public class IRNode { */ public static int getTypeSizeInBytes(String typeString) { return switch (typeString) { - case TYPE_BYTE -> 1; - case TYPE_CHAR, TYPE_SHORT -> 2; - case TYPE_INT, TYPE_FLOAT -> 4; - case TYPE_LONG, TYPE_DOUBLE -> 8; - default -> 0; + case TYPE_BYTE, TYPE_BOOLEAN -> 1; + case TYPE_CHAR, TYPE_SHORT -> 2; + case TYPE_INT, TYPE_FLOAT -> 4; + case TYPE_LONG, TYPE_DOUBLE -> 8; + default -> 0; }; } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/IRMethod.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/IRMethod.java index 5fbe90f0e72..137766b0011 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/IRMethod.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/IRMethod.java @@ -31,8 +31,8 @@ import compiler.lib.ir_framework.driver.irmatching.MatchResult; import compiler.lib.ir_framework.driver.irmatching.Matchable; import compiler.lib.ir_framework.driver.irmatching.MatchableMatcher; import compiler.lib.ir_framework.driver.irmatching.irrule.IRRule; -import compiler.lib.ir_framework.driver.irmatching.parser.VMInfo; import compiler.lib.ir_framework.driver.network.testvm.java.IRRuleIds; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import compiler.lib.ir_framework.shared.TestFormat; import compiler.lib.ir_framework.shared.TestFormatException; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/IRRule.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/IRRule.java index 749942eabd6..5c48394851b 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/IRRule.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/IRRule.java @@ -31,7 +31,7 @@ import compiler.lib.ir_framework.driver.irmatching.Matchable; import compiler.lib.ir_framework.driver.irmatching.MatchableMatcher; import compiler.lib.ir_framework.driver.irmatching.irrule.phase.CompilePhaseIRRule; import compiler.lib.ir_framework.driver.irmatching.irrule.phase.CompilePhaseIRRuleBuilder; -import compiler.lib.ir_framework.driver.irmatching.parser.VMInfo; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; /** * This class represents a generic {@link IR @IR} rule of an IR method. It contains a list of compile phase specific diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/checkattribute/parsing/RawIRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/checkattribute/parsing/RawIRNode.java index b86ae47e186..d8be8add2a2 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/checkattribute/parsing/RawIRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/checkattribute/parsing/RawIRNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -25,10 +25,10 @@ package compiler.lib.ir_framework.driver.irmatching.irrule.checkattribute.parsin import compiler.lib.ir_framework.CompilePhase; import compiler.lib.ir_framework.IRNode; +import compiler.lib.ir_framework.driver.SuccessOnlyConstraintException; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import compiler.lib.ir_framework.shared.Comparison; import compiler.lib.ir_framework.shared.TestFormat; -import compiler.lib.ir_framework.driver.irmatching.parser.VMInfo; -import compiler.lib.ir_framework.driver.SuccessOnlyConstraintException; import java.util.regex.Matcher; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawConstraint.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawConstraint.java index 1c2fba218db..174136c7c28 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawConstraint.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawConstraint.java @@ -27,12 +27,12 @@ import compiler.lib.ir_framework.CompilePhase; import compiler.lib.ir_framework.IR; import compiler.lib.ir_framework.IRNode; import compiler.lib.ir_framework.driver.irmatching.irrule.constraint.Constraint; -import compiler.lib.ir_framework.driver.irmatching.parser.VMInfo; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; /** * Interface to represent a single raw constraint as found in the {@link IR @IR} annotation (i.e. {@link IRNode} * placeholder strings are not replaced by regexes, yet). A raw constraint can be parsed into a {@link Constraint} by - * calling {@link #parse(CompilePhase, String)}. This replaces the IR node placeholder strings by actual regexes and + * calling {@link #parse(CompilePhase, String, VMInfo)}. This replaces the IR node placeholder strings by actual regexes and * merges composite nodes together. * * @see Constraint diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawCountsConstraint.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawCountsConstraint.java index 9934a830a06..a616f1a0455 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawCountsConstraint.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawCountsConstraint.java @@ -26,12 +26,11 @@ package compiler.lib.ir_framework.driver.irmatching.irrule.constraint.raw; import compiler.lib.ir_framework.CompilePhase; import compiler.lib.ir_framework.IR; -import compiler.lib.ir_framework.IRNode; import compiler.lib.ir_framework.TestFramework; import compiler.lib.ir_framework.driver.irmatching.irrule.checkattribute.parsing.RawIRNode; import compiler.lib.ir_framework.driver.irmatching.irrule.constraint.Constraint; -import compiler.lib.ir_framework.driver.irmatching.parser.VMInfo; import compiler.lib.ir_framework.driver.SuccessOnlyConstraintException; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import compiler.lib.ir_framework.shared.Comparison; import compiler.lib.ir_framework.shared.TestFormat; import compiler.lib.ir_framework.shared.TestFormatException; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawFailOnConstraint.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawFailOnConstraint.java index 4bd334f1f08..6bd3f8f3448 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawFailOnConstraint.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawFailOnConstraint.java @@ -29,7 +29,7 @@ import compiler.lib.ir_framework.TestFramework; import compiler.lib.ir_framework.driver.SuccessOnlyConstraintException; import compiler.lib.ir_framework.driver.irmatching.irrule.checkattribute.parsing.RawIRNode; import compiler.lib.ir_framework.driver.irmatching.irrule.constraint.Constraint; -import compiler.lib.ir_framework.driver.irmatching.parser.VMInfo; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import compiler.lib.ir_framework.shared.Comparison; /** diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/phase/CompilePhaseIRRuleBuilder.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/phase/CompilePhaseIRRuleBuilder.java index 5d327135c22..4d46a53c46b 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/phase/CompilePhaseIRRuleBuilder.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/phase/CompilePhaseIRRuleBuilder.java @@ -34,7 +34,7 @@ import compiler.lib.ir_framework.driver.irmatching.irrule.checkattribute.parsing import compiler.lib.ir_framework.driver.irmatching.irrule.checkattribute.parsing.RawFailOn; import compiler.lib.ir_framework.driver.irmatching.irrule.constraint.Constraint; import compiler.lib.ir_framework.driver.irmatching.irrule.constraint.raw.RawConstraint; -import compiler.lib.ir_framework.driver.irmatching.parser.VMInfo; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import compiler.lib.ir_framework.shared.TestFormat; import java.util.*; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/phase/DefaultPhaseRawConstraintParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/phase/DefaultPhaseRawConstraintParser.java index 1f837883978..5f8f597a4fe 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/phase/DefaultPhaseRawConstraintParser.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/phase/DefaultPhaseRawConstraintParser.java @@ -31,7 +31,7 @@ import compiler.lib.ir_framework.driver.irmatching.irrule.checkattribute.Counts; import compiler.lib.ir_framework.driver.irmatching.irrule.checkattribute.FailOn; import compiler.lib.ir_framework.driver.irmatching.irrule.constraint.Constraint; import compiler.lib.ir_framework.driver.irmatching.irrule.constraint.raw.RawConstraint; -import compiler.lib.ir_framework.driver.irmatching.parser.VMInfo; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import compiler.lib.ir_framework.shared.TestFrameworkException; import java.util.ArrayList; @@ -71,7 +71,7 @@ class DefaultPhaseRawConstraintParser { for (RawConstraint rawConstraint : rawConstraints) { CompilePhase compilePhase = rawConstraint.defaultCompilePhase(); List checkAttribute = - matchableForCompilePhase.computeIfAbsent(compilePhase, k -> new ArrayList<>()); + matchableForCompilePhase.computeIfAbsent(compilePhase, _ -> new ArrayList<>()); checkAttribute.add(rawConstraint.parse(compilePhase, compilation.output(compilePhase), vmInfo)); } return replaceConstraintsWithCheckAttribute(matchableForCompilePhase, checkAttributeType); @@ -113,7 +113,7 @@ class DefaultPhaseRawConstraintParser { private static void addCheckAttribute(Map failOnForCompilePhase, Map> result) { failOnForCompilePhase.forEach((compilePhase, matchable) -> { - List list = result.computeIfAbsent(compilePhase, k -> new ArrayList<>()); + List list = result.computeIfAbsent(compilePhase, _ -> new ArrayList<>()); list.add(matchable); }); } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java index d251c574e47..67a04df2b58 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java @@ -25,16 +25,12 @@ package compiler.lib.ir_framework.driver.irmatching.parser; import compiler.lib.ir_framework.IR; import compiler.lib.ir_framework.TestFramework; -import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.HotSpotPidFileParser; +import compiler.lib.ir_framework.driver.network.testvm.java.ApplicableIRRules; import compiler.lib.ir_framework.driver.network.testvm.java.IRRuleIds; import compiler.lib.ir_framework.shared.TestFormat; -import compiler.lib.ir_framework.shared.TestFrameworkException; -import compiler.lib.ir_framework.test.ApplicableIRRulesPrinter; import java.lang.reflect.Method; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; /** @@ -55,73 +51,19 @@ public class ApplicableIRRulesParser { * Parse the Applicable IR rules passed as parameter and return a "test name" -> TestMethod map that contains an * entry for each method that needs to be IR matched on. */ - public TestMethods parse(String applicableIRRules) { - createTestMethodMap(applicableIRRules, testClass); + public TestMethods parse(ApplicableIRRules applicableIRRules) { + createTestMethodMap(applicableIRRules); // We could have found format errors in @IR annotations. Report them now with an exception. TestFormat.throwIfAnyFailures(); return new TestMethods(testMethods); } - /** - * Sets up a map testname -> TestMethod map. The TestMethod object will later be filled with the ideal and opto - * assembly output in {@link HotSpotPidFileParser}. - */ - private void createTestMethodMap(String applicableIRRules, Class testClass) { - Map irRulesMap = parseApplicableIRRules(applicableIRRules); - createTestMethodsWithApplicableIRRules(testClass, irRulesMap); - } - - /** - * Read the Applicable IR Rules emitted by the Test VM to decide if an @IR rule must be checked for a method. - */ - private Map parseApplicableIRRules(String applicableIRRules) { - Map irRulesMap = new HashMap<>(); - String[] applicableIRRulesLines = getApplicableIRRulesLines(applicableIRRules); - for (String s : applicableIRRulesLines) { - String line = s.trim(); - String[] splitLine = line.split(","); - if (splitLine.length < 2) { - throw new TestFrameworkException("Invalid Applicable IR Rules format. No comma found: " + splitLine[0]); - } - String testName = splitLine[0]; - IRRuleIds irRuleIds = parseIrRulesIds(splitLine); - irRulesMap.put(testName, irRuleIds); - } - return irRulesMap; - } - - /** - * Parse the Applicable IR Rules lines without header, explanation line and footer and return them in an array. - */ - private String[] getApplicableIRRulesLines(String applicableIRRules) { - if (applicableIRRules.isEmpty()) { - // Nothing to IR match. - return new String[0]; - } - return applicableIRRules.split("\\R"); - } - - /** - * Parse rule indexes from a single line of the Applicable IR Rules in the format: - */ - private IRRuleIds parseIrRulesIds(String[] splitLine) { - List irRuleIds = new ArrayList<>(); - for (int i = 1; i < splitLine.length; i++) { - try { - irRuleIds.add(Integer.parseInt(splitLine[i])); - } catch (NumberFormatException e) { - throw new TestFrameworkException("Invalid Applicable IR Rules format. No number found: " + splitLine[i]); - } - } - return new IRRuleIds(irRuleIds); - } - - private void createTestMethodsWithApplicableIRRules(Class testClass, Map irRulesMap) { + private void createTestMethodMap(ApplicableIRRules applicableIRRules) { for (Method m : testClass.getDeclaredMethods()) { IR[] irAnnos = m.getAnnotationsByType(IR.class); if (irAnnos.length > 0) { // Validation of legal @IR attributes and placement of the annotation was already done in Test VM. - IRRuleIds irRuleIds = irRulesMap.get(m.getName()); + IRRuleIds irRuleIds = applicableIRRules.ruleIds(m.getName()); validateIRRuleIds(m, irAnnos, irRuleIds); if (hasAnyApplicableIRRules(irRuleIds)) { testMethods.put(m.getName(), new TestMethod(m, irAnnos, irRuleIds)); @@ -131,10 +73,7 @@ public class ApplicableIRRulesParser { } private void validateIRRuleIds(Method m, IR[] irAnnos, IRRuleIds irRuleIds) { - TestFramework.check(irRuleIds != null, "Should find method name in validIrRulesMap for " + m); - TestFramework.check(!irRuleIds.isEmpty(), "Did not find any rule indices for " + m); - TestFramework.check((irRuleIds.first() >= 1 || irRuleIds.first() == ApplicableIRRulesPrinter.NO_RULES) - && irRuleIds.last() <= irAnnos.length, + TestFramework.check((irRuleIds.isEmpty() || (irRuleIds.first() >= 1 && irRuleIds.last() <= irAnnos.length)), "Invalid IR rule index found in validIrRulesMap for " + m); } @@ -142,6 +81,6 @@ public class ApplicableIRRulesParser { * Does the list of IR rules contain any applicable IR rules for the given conditions? */ private boolean hasAnyApplicableIRRules(IRRuleIds irRuleIds) { - return irRuleIds.first() != ApplicableIRRulesPrinter.NO_RULES; + return !irRuleIds.isEmpty(); } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IRMethodBuilder.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IRMethodBuilder.java index 46a237576e6..5ce93df434c 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IRMethodBuilder.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IRMethodBuilder.java @@ -32,6 +32,7 @@ import compiler.lib.ir_framework.driver.irmatching.irmethod.NotCompilableIRMetho import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.HotSpotPidFileParser; import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.LoggedMethod; import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.LoggedMethods; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import java.util.Map; import java.util.SortedSet; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestClassParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestClassParser.java index ca36a3e9f72..30a766ddd4e 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestClassParser.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestClassParser.java @@ -23,6 +23,7 @@ package compiler.lib.ir_framework.driver.irmatching.parser; +import compiler.lib.ir_framework.TestFramework; import compiler.lib.ir_framework.driver.irmatching.Matchable; import compiler.lib.ir_framework.driver.irmatching.NonIRTestClass; import compiler.lib.ir_framework.driver.irmatching.TestClass; @@ -31,6 +32,8 @@ import compiler.lib.ir_framework.driver.irmatching.irmethod.IRMethodMatchable; import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.HotSpotPidFileParser; import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.LoggedMethods; import compiler.lib.ir_framework.driver.network.TestVMData; +import compiler.lib.ir_framework.driver.network.testvm.java.ApplicableIRRules; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import compiler.lib.ir_framework.shared.TestFormat; import java.util.SortedSet; @@ -55,19 +58,20 @@ public class TestClassParser { * Return a default/empty TestClass object if there are no applicable @IR rules in any method of the test class. */ public Matchable parse(TestVMData testVmData) { + ApplicableIRRules applicableIrRules = testVmData.applicableIRRules(); + if (applicableIrRules.hasNoMethods()) { + return new NonIRTestClass(); + } ApplicableIRRulesParser applicableIRRulesParser = new ApplicableIRRulesParser(testClass); TestMethods testMethods = applicableIRRulesParser.parse(testVmData.applicableIRRules()); - VMInfo vmInfo = VMInfoParser.parseVMInfo(testVmData.vmInfo()); - if (testMethods.hasTestMethods()) { - HotSpotPidFileParser hotSpotPidFileParser = new HotSpotPidFileParser(testClass.getName(), testMethods); - LoggedMethods loggedMethods = hotSpotPidFileParser.parse(testVmData.hotspotPidFileName()); - return createTestClass(testMethods, loggedMethods, vmInfo); - } - return new NonIRTestClass(); + TestFramework.check(testMethods.hasTestMethods(), "must have at least one"); + HotSpotPidFileParser hotSpotPidFileParser = new HotSpotPidFileParser(testClass.getName(), testMethods); + LoggedMethods loggedMethods = hotSpotPidFileParser.parse(testVmData.hotspotPidFileName()); + return createTestClass(testMethods, loggedMethods, testVmData.vmInfo()); } /** - * Create test class with IR methods for all test methods identified by {@link ApplicableIRRulesParser} by combining them + * Create test class with IR methods for all test methods found in {@link ApplicableIRRules} by combining them * with the parsed compilation output from {@link HotSpotPidFileParser}. */ private Matchable createTestClass(TestMethods testMethods, LoggedMethods loggedMethods, VMInfo vmInfo) { diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfoParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfoParser.java deleted file mode 100644 index 44013839754..00000000000 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfoParser.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2023, 2026, 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.ir_framework.driver.irmatching.parser; - -import compiler.lib.ir_framework.shared.TestFrameworkException; - -import java.util.HashMap; -import java.util.Map; - -/** - * Class to parse the VMInfo emitted by the Test VM and creating {@link VMInfo} objects for each entry. - * - * @see VMInfo - */ -public class VMInfoParser { - - /** - * Create a new VMInfo object from the vmInfo string. - */ - public static VMInfo parseVMInfo(String vmInfo) { - Map map = new HashMap<>(); - String[] lines = getVMInfoLines(vmInfo); - for (String s : lines) { - String line = s.trim(); - String[] splitLine = line.split(":", 2); - if (splitLine.length != 2) { - throw new TestFrameworkException("Invalid VMInfo key:value encoding. Found: " + splitLine[0]); - } - String key = splitLine[0]; - String value = splitLine[1]; - map.put(key, value); - } - return new VMInfo(map); - } - - /** - * Extract the VMInfo from the applicableIRRules string, strip away the header and return the individual key-value lines. - */ - private static String[] getVMInfoLines(String vmInfo) { - if (vmInfo.isEmpty()) { - // Nothing to IR match. - return new String[0]; - } - return vmInfo.split("\\R"); - } -} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/TestVMData.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/TestVMData.java index 413cf3347d8..59632ef1a48 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/TestVMData.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/TestVMData.java @@ -24,7 +24,9 @@ package compiler.lib.ir_framework.driver.network; import compiler.lib.ir_framework.driver.irmatching.IRMatcher; +import compiler.lib.ir_framework.driver.network.testvm.java.ApplicableIRRules; import compiler.lib.ir_framework.driver.network.testvm.java.JavaMessages; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import compiler.lib.ir_framework.shared.TestFrameworkSocket; /** @@ -42,11 +44,11 @@ public class TestVMData { this.allowNotCompilable = allowNotCompilable; } - public String applicableIRRules() { + public ApplicableIRRules applicableIRRules() { return javaMessages.applicableIRRules(); } - public String vmInfo() { + public VMInfo vmInfo() { return javaMessages.vmInfo(); } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/ApplicableIRRules.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/ApplicableIRRules.java new file mode 100644 index 00000000000..eb41472057e --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/ApplicableIRRules.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2026, 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.ir_framework.driver.network.testvm.java; + +import compiler.lib.ir_framework.IR; +import compiler.lib.ir_framework.TestFramework; +import compiler.lib.ir_framework.driver.irmatching.IRMatcher; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Class to hold the Applicable IR Rules sent by the Test VM. It specifies which {@link IR @IR} rules the + * {@link IRMatcher} need to check. This can be different depending on the used VM flags or the machine the test is run + * on itself. + */ +public class ApplicableIRRules implements JavaMessage { + private static final boolean PRINT_APPLICABLE_IR_RULES = + Boolean.parseBoolean(System.getProperty("PrintApplicableIRRules", "false")) + || TestFramework.VERBOSE; + + private final Map methods; + + public ApplicableIRRules() { + this.methods = new LinkedHashMap<>(); + } + + public void add(String method, IRRuleIds irRuleIds) { + methods.put(method, irRuleIds); + } + + public IRRuleIds ruleIds(String methodName) { + return methods.computeIfAbsent(methodName, _ -> IRRuleIds.createEmpty()); + } + + public boolean hasNoMethods() { + return methods.isEmpty(); + } + + @Override + public void print() { + if (!PRINT_APPLICABLE_IR_RULES) { + return; + } + + System.out.println(); + System.out.println("Applicable IR Rules"); + System.out.println("-------------------"); + if (methods.isEmpty()) { + System.out.println(""); + return; + } + for (var entry : methods.entrySet()) { + String method = entry.getKey(); + String ruleIds = entry.getValue().stream().map(String::valueOf).collect(Collectors.joining(", ")); + System.out.println("- " + method + ": " + ruleIds); + } + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/IRRuleIds.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/IRRuleIds.java index b8ea1765b4f..f5bc084fbce 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/IRRuleIds.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/IRRuleIds.java @@ -49,6 +49,10 @@ public class IRRuleIds implements Iterable { return ruleIds.getLast(); } + public static IRRuleIds createEmpty() { + return new IRRuleIds(new ArrayList<>()); + } + public boolean isEmpty() { return ruleIds.isEmpty(); } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessageParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessageParser.java index 896aef38f1f..d419a06c8de 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessageParser.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessageParser.java @@ -24,6 +24,9 @@ package compiler.lib.ir_framework.driver.network.testvm.java; import compiler.lib.ir_framework.TestFramework; +import compiler.lib.ir_framework.driver.network.testvm.java.multiline.ApplicableIRRulesStrategy; +import compiler.lib.ir_framework.driver.network.testvm.java.multiline.MultiLineParser; +import compiler.lib.ir_framework.driver.network.testvm.java.multiline.VMInfoStrategy; import compiler.lib.ir_framework.shared.TestFrameworkException; import compiler.lib.ir_framework.test.network.MessageTag; @@ -43,18 +46,18 @@ public class JavaMessageParser { private final List stdoutMessages; private final List executedTests; private final Map methodTimes; - private final StringBuilder vmInfoBuilder; - private final StringBuilder applicableIrRules; + private final MultiLineParser vmInfoParser; + private final MultiLineParser applicableIRRulesParser; - private StringBuilder currentBuilder; + private MultiLineParser currentMultiLineParser; public JavaMessageParser() { this.stdoutMessages = new ArrayList<>(); this.methodTimes = new HashMap<>(); this.executedTests = new ArrayList<>(); - this.vmInfoBuilder = new StringBuilder(); - this.applicableIrRules = new StringBuilder(); - this.currentBuilder = null; + this.vmInfoParser = new MultiLineParser<>(new VMInfoStrategy()); + this.applicableIRRulesParser = new MultiLineParser<>(new ApplicableIRRulesStrategy()); + this.currentMultiLineParser = null; } public void parseLine(String line) { @@ -74,12 +77,11 @@ public class JavaMessageParser { return; } - // Multi-line message for single tag. - currentBuilder.append(line).append(System.lineSeparator()); + currentMultiLineParser.parseLine(line); } private void assertNoActiveParser() { - TestFramework.check(currentBuilder == null, "Unexpected new tag while parsing block"); + TestFramework.check(currentMultiLineParser == null, "Unexpected new tag while parsing block"); } private void parseTagLine(Matcher tagLineMatcher) { @@ -89,8 +91,8 @@ public class JavaMessageParser { case STDOUT -> stdoutMessages.add(message); case TEST_LIST -> executedTests.add(message); case PRINT_TIMES -> parsePrintTimes(message); - case VM_INFO -> currentBuilder = vmInfoBuilder; - case APPLICABLE_IR_RULES -> currentBuilder = applicableIrRules; + case VM_INFO -> currentMultiLineParser = vmInfoParser; + case APPLICABLE_IR_RULES -> currentMultiLineParser = applicableIRRulesParser; default -> throw new TestFrameworkException("unknown tag"); } } @@ -108,18 +110,19 @@ public class JavaMessageParser { } private void assertActiveParser() { - TestFramework.check(currentBuilder != null, "Received non-tag line outside of any tag block"); + TestFramework.check(currentMultiLineParser != null, "Received non-tag line outside of any tag block"); } private void parseEndTag() { - currentBuilder = null; + currentMultiLineParser.markFinished(); + currentMultiLineParser = null; } public JavaMessages output() { return new JavaMessages(new StdoutMessages(stdoutMessages), new ExecutedTests(executedTests), new MethodTimes(methodTimes), - applicableIrRules.toString(), - vmInfoBuilder.toString()); + applicableIRRulesParser.output(), + vmInfoParser.output()); } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessages.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessages.java index e47ecff4b2a..e817610f441 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessages.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessages.java @@ -23,22 +23,18 @@ package compiler.lib.ir_framework.driver.network.testvm.java; -import compiler.lib.ir_framework.TestFramework; - /** * Class to collect all Java messages sent from the Test VM to the Driver VM. */ public class JavaMessages { - private static final boolean PRINT_APPLICABLE_IR_RULES = Boolean.parseBoolean(System.getProperty("PrintApplicableIRRules", "false")); - private final StdoutMessages stdoutMessages; private final ExecutedTests executedTests; private final MethodTimes methodTimes; - private final String applicableIrRules; - private final String vmInfo; + private final ApplicableIRRules applicableIrRules; + private final VMInfo vmInfo; JavaMessages(StdoutMessages stdoutMessages, ExecutedTests executedTests, MethodTimes methodTimes, - String applicableIrRules, String vmInfo) { + ApplicableIRRules applicableIrRules, VMInfo vmInfo) { this.stdoutMessages = stdoutMessages; this.executedTests = executedTests; this.methodTimes = methodTimes; @@ -46,21 +42,19 @@ public class JavaMessages { this.vmInfo = vmInfo; } - public String applicableIRRules() { - return applicableIrRules; + public VMInfo vmInfo() { + return vmInfo; } - public String vmInfo() { - return vmInfo; + public ApplicableIRRules applicableIRRules() { + return applicableIrRules; } public void print() { stdoutMessages.print(); methodTimes.print(); executedTests.print(); - if (TestFramework.VERBOSE || PRINT_APPLICABLE_IR_RULES) { - System.out.println("Read Applicable IR Rules from Test VM:"); - System.out.println(applicableIrRules); - } + vmInfo.print(); + applicableIrRules.print(); } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfo.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/VMInfo.java similarity index 74% rename from test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfo.java rename to test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/VMInfo.java index 89b6d610496..a948dbc22ef 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfo.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/VMInfo.java @@ -21,7 +21,7 @@ * questions. */ -package compiler.lib.ir_framework.driver.irmatching.parser; +package compiler.lib.ir_framework.driver.network.testvm.java; import compiler.lib.ir_framework.TestFramework; import compiler.lib.ir_framework.shared.TestFrameworkException; @@ -32,31 +32,27 @@ import java.util.regex.Pattern; /** * This class stores the key value mapping from the VMInfo. - * - * @see ApplicableIRRulesParser */ -public class VMInfo { +public class VMInfo implements JavaMessage { + private static final Pattern CPU_SKYLAKE_PATTERN = + Pattern.compile("family 6 model 85 stepping (\\d+) "); + /** * Stores the key-value mapping. */ private final Map keyValueMap; - private static final Pattern CPU_SKYLAKE_PATTERN = - Pattern.compile("family 6 model 85 stepping (\\d+) "); - - public VMInfo(Map map) { - this.keyValueMap = map; - - TestFramework.check(isKey("cpuFeatures"), "VMInfo does not contain cpuFeatures"); - TestFramework.check(isKey("MaxVectorSize"), "VMInfo does not contain MaxVectorSize"); - TestFramework.check(isKey("MaxVectorSizeIsDefault"), "VMInfo does not contain MaxVectorSizeIsDefault"); - TestFramework.check(isKey("LoopMaxUnroll"), "VMInfo does not contain LoopMaxUnroll"); - TestFramework.check(isKey("UseAVX"), "VMInfo does not contain UseAVX"); - TestFramework.check(isKey("UseAVXIsDefault"), "VMInfo does not contain UseAVXIsDefault"); + public VMInfo(Map keyValueMap) { + this.keyValueMap = keyValueMap; } - public String getStringValue(String key) { - TestFramework.check(isKey(key), "VMInfo does not contain \"" + key + "\""); + public boolean hasCPUFeature(String feature) { + String features = getStringValue("cpuFeatures") + ","; + return features.contains(" " + feature + ","); + } + + private String getStringValue(String key) { + TestFramework.check(keyValueMap.containsKey(key), "VMInfo does not contain \"" + key + "\""); return keyValueMap.get(key); } @@ -68,28 +64,6 @@ public class VMInfo { } } - public boolean hasCPUFeature(String feature) { - String features = getStringValue("cpuFeatures") + ","; - return features.contains(" " + feature + ","); - } - - public boolean isCascadeLake() { - Matcher matcher = CPU_SKYLAKE_PATTERN.matcher(getStringValue("cpuFeatures")); - if (!matcher.find()) { - return false; // skylake pattern not found - } - String stepping = matcher.group(1).trim(); - return Long.parseLong(stepping) >= 5; // this makes it Cascade Lake - } - - public boolean isDefaultCascadeLake() { - // See VM_Version::is_default_intel_cascade_lake - return isCascadeLake() && - getLongValue("MaxVectorSizeIsDefault") == 1 && - getLongValue("UseAVXIsDefault") == 1 && - getLongValue("UseAVX") > 2; - } - /** * Some platforms do not behave as expected, and one cannot trust that the vectors * make use of the full MaxVectorSize. For Cascade Lake, we only use 32 bytes for @@ -101,7 +75,36 @@ public class VMInfo { return !isDefaultCascadeLake(); } - public boolean isKey(String key) { - return keyValueMap.containsKey(key); + private boolean isDefaultCascadeLake() { + // See VM_Version::is_default_intel_cascade_lake + return isCascadeLake() && + getLongValue("MaxVectorSizeIsDefault") == 1 && + getLongValue("UseAVXIsDefault") == 1 && + getLongValue("UseAVX") > 2; + } + + private boolean isCascadeLake() { + Matcher matcher = CPU_SKYLAKE_PATTERN.matcher(getStringValue("cpuFeatures")); + if (!matcher.find()) { + return false; // skylake pattern not found + } + String stepping = matcher.group(1).trim(); + return Long.parseLong(stepping) >= 5; // this makes it Cascade Lake + } + + @Override + public void print() { + if (!TestFramework.VERBOSE) { + return; + } + + System.out.println(); + System.out.println("VM Info"); + System.out.println("--------"); + for (var entry : keyValueMap.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + System.out.println("- Key: " + key + ", Value: " + value); + } } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/ApplicableIRRulesStrategy.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/ApplicableIRRulesStrategy.java new file mode 100644 index 00000000000..3728e489ba7 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/ApplicableIRRulesStrategy.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2026, 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.ir_framework.driver.network.testvm.java.multiline; + +import compiler.lib.ir_framework.driver.network.testvm.java.ApplicableIRRules; +import compiler.lib.ir_framework.driver.network.testvm.java.IRRuleIds; +import compiler.lib.ir_framework.shared.TestFrameworkException; +import compiler.lib.ir_framework.test.ApplicableIRRulesPrinter; + +import java.util.ArrayList; +import java.util.List; + +/** + * Dedicated strategy to parse the multi-line Applicable IR Rules message into a new {@link ApplicableIRRules} object. + */ +public class ApplicableIRRulesStrategy implements MultiLineParsingStrategy { + private final ApplicableIRRules applicableIrRules; + + public ApplicableIRRulesStrategy() { + this.applicableIrRules = new ApplicableIRRules(); + } + + @Override + public void parseLine(String line) { + if (line.equals(ApplicableIRRulesPrinter.NO_RULES)) { + return; + } + + String[] splitLine = line.split(","); + if (splitLine.length < 2) { + throw new TestFrameworkException("Invalid Applicable IR Rules format. No comma found: " + splitLine[0]); + } + String testName = splitLine[0]; + IRRuleIds irRulesIds = parseIrRulesIds(splitLine); + applicableIrRules.add(testName, irRulesIds); + } + + /** + * Parse rule indexes from a single line of the Applicable IR Rules in the format: + */ + private IRRuleIds parseIrRulesIds(String[] splitLine) { + List irRuleIds = new ArrayList<>(); + for (int i = 1; i < splitLine.length; i++) { + try { + irRuleIds.add(Integer.parseInt(splitLine[i])); + } catch (NumberFormatException e) { + throw new TestFrameworkException("Invalid Applicable IR Rules format. No number found: " + splitLine[i]); + } + } + return new IRRuleIds(irRuleIds); + } + + @Override + public ApplicableIRRules output() { + return applicableIrRules; + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/MultiLineParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/MultiLineParser.java new file mode 100644 index 00000000000..fbc24eed736 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/MultiLineParser.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2026, 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.ir_framework.driver.network.testvm.java.multiline; + +import compiler.lib.ir_framework.TestFramework; +import compiler.lib.ir_framework.driver.network.testvm.java.JavaMessage; +import compiler.lib.ir_framework.test.network.MessageTag; + +/** + * Generic multi-line parser that takes a {@link MultiLineParsingStrategy} to decide how to parse a single line of + * a multi line {@link JavaMessage}. Once parsing is done, the strategy is queried for the final parsed output. + */ +public class MultiLineParser { + private enum ParserState { + NOTHING_PARSED, PARSING, FINISHED_PARSING + } + + private ParserState parserState; + private final MultiLineParsingStrategy multiLineParsingStrategy; + + public MultiLineParser(MultiLineParsingStrategy multiLineParsingStrategy) { + this.multiLineParsingStrategy = multiLineParsingStrategy; + this.parserState = ParserState.NOTHING_PARSED; + } + + public void parseLine(String line) { + TestFramework.check(parserState != ParserState.FINISHED_PARSING, + "cannot parse another block"); + parserState = ParserState.PARSING; + multiLineParsingStrategy.parseLine(line); + } + + /** + * Once the {@link MessageTag#END_MARKER} was seen, this method is called to mark the end of this multi-line message. + */ + public void markFinished() { + TestFramework.check(parserState == ParserState.PARSING, + "nothing parsed, cannot have empty block"); + parserState = ParserState.FINISHED_PARSING; + } + + public Output output() { + TestFramework.check(parserState != ParserState.PARSING, "either nothing parsed or finished"); + return multiLineParsingStrategy.output(); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/MultiLineParsingStrategy.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/MultiLineParsingStrategy.java new file mode 100644 index 00000000000..0283c520465 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/MultiLineParsingStrategy.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2026, 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.ir_framework.driver.network.testvm.java.multiline; + +/** + * Interface to define a strategy to parse a line in a {@link MultiLineParser}. + */ +public interface MultiLineParsingStrategy { + void parseLine(String line); + Output output(); +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/VMInfoStrategy.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/VMInfoStrategy.java new file mode 100644 index 00000000000..7d3072394c0 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/VMInfoStrategy.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2026, 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.ir_framework.driver.network.testvm.java.multiline; + +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; +import compiler.lib.ir_framework.shared.TestFrameworkException; + +import java.util.HashMap; +import java.util.Map; + +/** + * Dedicated strategy to parse the multi-line VM info message into a new {@link VMInfo} object. + */ +public class VMInfoStrategy implements MultiLineParsingStrategy { + private final Map keyValueMap; + + public VMInfoStrategy() { + this.keyValueMap = new HashMap<>(); + } + + @Override + public void parseLine(String line) { + String[] splitLine = line.split(":", 2); + if (splitLine.length != 2) { + throw new TestFrameworkException("Invalid VmInfo key:value encoding. Found: " + splitLine[0]); + } + String key = splitLine[0]; + String value = splitLine[1]; + keyValueMap.put(key, value); + } + + @Override + public VMInfo output() { + return new VMInfo(keyValueMap); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java index 79f5c7fe18e..a4797a29dc7 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java @@ -47,7 +47,7 @@ import java.util.function.Function; * termination of the Test VM. IR rule indices start at 1. */ public class ApplicableIRRulesPrinter { - public static final int NO_RULES = -1; + public static final String NO_RULES = ""; private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); private static final List> LONG_GETTERS = Arrays.asList( @@ -155,17 +155,15 @@ public class ApplicableIRRulesPrinter { i++; } } - if (irAnnos.length != 0) { - output.append(m.getName()); - if (validRules.isEmpty()) { - output.append("," + NO_RULES); - } else { - for (i = 0; i < validRules.size(); i++) { - output.append(",").append(validRules.get(i)); - } - } - output.append(System.lineSeparator()); + + if (irAnnos.length == 0 || validRules.isEmpty()) { + return; } + output.append(m.getName()); + for (i = 0; i < validRules.size(); i++) { + output.append(",").append(validRules.get(i)); + } + output.append(System.lineSeparator()); } private void printDisableReason(String method, String reason, String[] apply, int ruleIndex, int ruleMax) { @@ -520,9 +518,10 @@ public class ApplicableIRRulesPrinter { } public void emit() { + if (output.isEmpty()) { + output.append(NO_RULES).append(System.lineSeparator()); + } output.append(MessageTag.END_MARKER); TestVmSocket.sendMultiLine(MessageTag.APPLICABLE_IR_RULES, output.toString()); } } - - diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java index 43ab16af415..37ad4debde6 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java @@ -382,6 +382,23 @@ public class Expression { return sb.toString(); } + /** + * {@link Nesting} defines the different ways of selecting {@link Expression}s + * to nest based on their types. + */ + public enum Nesting { + /** + * Only nest {@Expression}s where the argument and return types match exactly + * based on the implementation of {@link CodeGenerateionDataNameType#isSubtypeOf}. + */ + EXACT, + /** + * Only nest {@Expression}s where the return type is a subtype of the argument + * type based on the implemetation of {@link CodeGenerateionDataNameType#isSubtypeOf}. + */ + SUBTYPE + } + /** * Create a nested {@link Expression} with a specified {@code returnType} from a * set of {@code expressions}. @@ -391,11 +408,13 @@ public class Expression { * the nested {@link Expression}. * @param maxNumberOfUsedExpressions the maximal number of {@link Expression}s from the * {@code expressions} are nested. + * @param nesting control the {@link Nesting} of the sampled {@link Expression}s. * @return a new randomly nested {@link Expression}. */ public static Expression nestRandomly(CodeGenerationDataNameType returnType, List expressions, - int maxNumberOfUsedExpressions) { + int maxNumberOfUsedExpressions, + Nesting nesting) { List filtered = expressions.stream().filter(e -> e.returnType.isSubtypeOf(returnType)).toList(); if (filtered.isEmpty()) { @@ -406,7 +425,7 @@ public class Expression { Expression expression = filtered.get(r); for (int i = 1; i < maxNumberOfUsedExpressions; i++) { - expression = expression.nestRandomly(expressions); + expression = expression.nestRandomly(expressions, nesting); } return expression; } @@ -416,12 +435,16 @@ public class Expression { * {@code this} {@link Expression}, ensuring compatibility of argument and return type. * * @param nestingExpressions list of expressions we sample from for the inner {@link Expression}. + * @param nesting control the {@link Nesting} of the sampled {@link Expression}s. * @return a new nested {@link Expression}. */ - public Expression nestRandomly(List nestingExpressions) { + public Expression nestRandomly(List nestingExpressions, Nesting nesting) { int argumentIndex = RANDOM.nextInt(this.argumentTypes.size()); CodeGenerationDataNameType argumentType = this.argumentTypes.get(argumentIndex); - List filtered = nestingExpressions.stream().filter(e -> e.returnType.isSubtypeOf(argumentType)).toList(); + List filtered = nestingExpressions.stream() + .filter(e -> e.returnType.isSubtypeOf(argumentType) && + (nesting == Nesting.EXACT ? argumentType.isSubtypeOf(e.returnType) : true)) + .toList(); if (filtered.isEmpty()) { // Found no expression that has a matching returnType. diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index 1fe05cc2b6c..2ea251cc5e5 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -129,8 +129,17 @@ public final class Operations { ops.add(Expression.make(type, "(", type, " + ", type, ")")); ops.add(Expression.make(type, "(", type, " - ", type, ")")); ops.add(Expression.make(type, "(", type, " * ", type, ")")); - ops.add(Expression.make(type, "(", type, " / ", type, ")")); - ops.add(Expression.make(type, "(", type, " % ", type, ")")); + // Because of subtyping, we can sample an expression like `(float)((int)(3) / (int)(0))`. Floating point + // division and modulo do not throw an ArithmeticException on division by zero, integer division and modulo + // do. In the expression above, the division has an integer on both sides, so it is executed as an integer + // division and throws an ArithmeticException even though we would expect the float division not to do so. + // To prevent this issue, we provide two versions of floating point division operations: one that casts + // its operands and one that expects that an ArithmeticException might be thrown when we get unlucky when + // sampling subtypes. + ops.add(Expression.make(type, "((" + type.name() + ")(", type, ") / (" + type.name() +")(", type, "))")); + ops.add(Expression.make(type, "((" + type.name() + ")(", type, ") % (" + type.name() +")(", type, "))")); + ops.add(Expression.make(type, "(", type, " / ", type, ")", WITH_ARITHMETIC_EXCEPTION)); + ops.add(Expression.make(type, "(", type, " % ", type, ")", WITH_ARITHMETIC_EXCEPTION)); // Relational / Comparison Operators ops.add(Expression.make(BOOLEANS, "(", type, " == ", type, ")")); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java index c8541ac1fa6..b20dbb28d22 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java @@ -71,7 +71,25 @@ public final class PrimitiveType implements CodeGenerationDataNameType { @Override public boolean isSubtypeOf(DataName.Type other) { - return (other instanceof PrimitiveType pt) && pt.kind == kind; + // Implement other >: this according to JLS §4.10.1. + if (other instanceof PrimitiveType superType) { + if (superType.kind == Kind.BOOLEAN || kind == Kind.BOOLEAN) { + // Boolean does not have a supertype and only itself as a subtype. + return superType.kind == this.kind; + } + if (superType.kind == Kind.CHAR || kind == Kind.CHAR) { + // Char does not have a subtype, but it is itself a subtype of any primitive type with + // a larger byte size. The following is correct for the subtype relation to floats, + // since chars are 16 bits wide and floats 32 bits or more. + return superType.kind == this.kind || (superType.byteSize() > this.byteSize() && this.kind != Kind.BYTE); + } + // Due to float >: long, all integers are subtypes of floating point types. + return (superType.isFloating() && !this.isFloating()) || + // Generally, narrower types are subtypes of wider types. + (superType.isFloating() == this.isFloating() && superType.byteSize() >= this.byteSize()); + } + + return false; } @Override diff --git a/test/hotspot/jtreg/compiler/longcountedloops/TestLoopNestTooManyTraps.java b/test/hotspot/jtreg/compiler/longcountedloops/TestLoopNestTooManyTraps.java index 502afc56548..c2008a2cccb 100644 --- a/test/hotspot/jtreg/compiler/longcountedloops/TestLoopNestTooManyTraps.java +++ b/test/hotspot/jtreg/compiler/longcountedloops/TestLoopNestTooManyTraps.java @@ -25,6 +25,7 @@ * @test * @bug 8350330 * @summary C2: PhaseIdealLoop::add_parse_predicate() should mirror GraphKit::add_parse_predicate() + * @requires vm.flagless * @library /test/lib / * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox diff --git a/test/hotspot/jtreg/compiler/loopopts/TestCountedLoopSafepointBackedge.java b/test/hotspot/jtreg/compiler/loopopts/TestCountedLoopSafepointBackedge.java index e277f15035a..31c32f2cd2a 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestCountedLoopSafepointBackedge.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestCountedLoopSafepointBackedge.java @@ -24,7 +24,7 @@ /** * @test * @bug 8161147 - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @summary Safepoint on backedge breaks UseCountedLoopSafepoints * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:+UseCountedLoopSafepoints TestCountedLoopSafepointBackedge * diff --git a/test/hotspot/jtreg/compiler/loopopts/TestLoopPeeling.java b/test/hotspot/jtreg/compiler/loopopts/TestLoopPeeling.java index a32f3cb851a..aab6e4f2aaa 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestLoopPeeling.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestLoopPeeling.java @@ -23,12 +23,27 @@ /* * @test - * @bug 8078262 8177095 - * @summary Tests correct dominator information after loop peeling. + * @bug 8078262 8177095 8371685 + * @summary Tests correct dominator information after loop peeling and + * tests that LoopPeeling flag correctly disables loop peeling. * * @run main/othervm -Xcomp * -XX:CompileCommand=compileonly,compiler.loopopts.TestLoopPeeling::test* * compiler.loopopts.TestLoopPeeling + * @run main/othervm -Xcomp + * -XX:+UnlockDiagnosticVMOptions -XX:LoopPeeling=0 + * -XX:CompileCommand=compileonly,compiler.loopopts.TestLoopPeeling::test* + * compiler.loopopts.TestLoopPeeling + * @run main/othervm -Xbatch + * -XX:+UnlockDiagnosticVMOptions -XX:LoopPeeling=0 + * compiler.loopopts.TestLoopPeeling + * @run main/othervm -Xcomp + * -XX:+UnlockDiagnosticVMOptions -XX:LoopPeeling=2 + * -XX:CompileCommand=compileonly,compiler.loopopts.TestLoopPeeling::test* + * compiler.loopopts.TestLoopPeeling + * @run main/othervm -Xbatch + * -XX:+UnlockDiagnosticVMOptions -XX:LoopPeeling=2 + * compiler.loopopts.TestLoopPeeling */ package compiler.loopopts; @@ -44,6 +59,8 @@ public class TestLoopPeeling { test.testArrayAccess2(0); test.testArrayAccess3(0, false); test.testArrayAllocation(0, 1); + test.testEmptyLoop(100); + test.testMaxUnrollOddTrip(); } catch (Exception e) { // Ignore exceptions } @@ -142,5 +159,19 @@ public class TestLoopPeeling { } return null; } -} + public void testEmptyLoop(int limit) { + for (int i = 0; i < limit; i++) { + // Empty body - candidate for empty loop removal + } + } + + public int testMaxUnrollOddTrip() { + int sum = 0; + // Trip count of 5 (odd) - maximal unroll needs to peel one iteration + for (int i = 0; i < 5; i++) { + sum += array[i]; + } + return sum; + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/TestLoopPeelingDisabled.java b/test/hotspot/jtreg/compiler/loopopts/TestLoopPeelingDisabled.java new file mode 100644 index 00000000000..e7bbe24d1de --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestLoopPeelingDisabled.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2026, 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.loopopts; + +import compiler.lib.ir_framework.*; +import compiler.lib.ir_framework.driver.irmatching.IRViolationException; +import jdk.test.lib.Asserts; + +/* + * @test + * @bug 8371685 + * @requires vm.flagless & vm.debug + * @summary Verifies that the LoopPeeling flag correctly disables loop peeling + * by checking whether the "After Loop Peeling" compile phase is + * emitted. When loop peeling is disabled, no peeling should occur and + * the phase must be absent from the compilation output. + * @library /test/lib / + * @run driver compiler.loopopts.TestLoopPeelingDisabled + */ +public class TestLoopPeelingDisabled { + static int[] array = new int[100]; + + public static void main(String[] args) { + // First, run the test with loop peeling enabled, which is the default. + // The IR framework should catch if the number of counted loops does not + // match the annotations. + TestFramework.run(); + + // Then, run the same test with loop peeling disabled, which should + // elide the {BEFORE,AFTER}_LOOP_PEELING compilation phases, causing the + // test to throw an IRViolationException. We then check whether the + // exception message matches our expectation (that the loop peeling + // phase was not found). + try { + TestFramework.runWithFlags("-XX:+UnlockDiagnosticVMOptions", + "-XX:LoopPeeling=0"); + Asserts.fail("Expected IRViolationException"); + } catch (IRViolationException e) { + String info = e.getExceptionInfo(); + if (!info.contains("NO compilation output found for this phase")) { + Asserts.fail("Unexpected IR violation: " + info); + } + System.out.println("Loop peeling correctly disabled"); + } + + // Finally, run the same test with loop peeling disabled only when + // splitting iterations. Since the function being tested does not hit + // this case, we expect that the loop will be peeled, which is ensured + // by the IR annotations. + TestFramework.runWithFlags("-XX:+UnlockDiagnosticVMOptions", + "-XX:LoopPeeling=2"); + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "1"}, phase = CompilePhase.BEFORE_LOOP_PEELING) + @IR(counts = {IRNode.COUNTED_LOOP, "2"}, phase = CompilePhase.AFTER_LOOP_PEELING) + public static int test() { + int sum = 0; + + // Use an odd trip count so that `do_maximally_unroll()` tries to peel + // the odd iteration. + for (int i = 0; i < 5; i++) { + sum += array[i]; + } + + return sum; + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/TestStressLongCountedLoopLimitChecks.java b/test/hotspot/jtreg/compiler/loopopts/TestStressLongCountedLoopLimitChecks.java new file mode 100644 index 00000000000..fd0143acdb6 --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestStressLongCountedLoopLimitChecks.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2026 IBM 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.loopopts; + +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +import java.io.IOException; +import java.util.Arrays; +import java.util.stream.Stream; + +/** + * @test + * @bug 8353290 + * @summary test loop limit checks are inserted when stressing int counted loops to long counted loops + * @library /test/lib + * @requires vm.debug == true + * @run driver compiler.loopopts.TestStressLongCountedLoopLimitChecks + */ +public class TestStressLongCountedLoopLimitChecks { + public static void main(String[] args) throws Exception { + test(BasicLauncher.class, 1, "-XX:StressLongCountedLoop=0"); + test(BasicLauncher.class, 1, "-XX:StressLongCountedLoop=2000000"); + + test(LargeStrideLauncher.class, 2, "-XX:StressLongCountedLoop=0"); + test(LargeStrideLauncher.class, 2, "-XX:StressLongCountedLoop=2000000"); + } + + private static void test(Class launcher, int limitChecks, String... flags) throws IOException { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + Stream.concat(Arrays.stream(flags), Stream.of( + "-XX:+IgnoreUnrecognizedVMOptions", + "-XX:+TraceLoopLimitCheck", + "-XX:CompileOnly=" + launcher.getName() + "::test*", + "-Xcomp", + launcher.getName() + )).toList() + ); + + OutputAnalyzer analyzer = new OutputAnalyzer(pb.start()); + analyzer.shouldHaveExitValue(0); + + analyzer.outputTo(System.out); + analyzer.errorTo(System.err); + + Asserts.assertEQ( + limitChecks, + (int) analyzer.asLines().stream().filter( + l -> l.trim().matches("Counted Loop Limit Check generated:") + ).count(), + "wrong numbers of loop limit checks" + ); + } + + public static class BasicLauncher { + static int x, y, z; + + public static void main(String[] args) throws Exception { + test(); + } + + static void test() { + int i = x; // Any int + do { + x += y; + i++; // Could overflow and thus we need a Loop Limit Check Predicate "i < z" + } while (i < z); + } + } + + public static class LargeStrideLauncher { + static final int STRIDE = 100_000; + + public static void main(String[] args) throws Exception { + Asserts.assertEQ(10_000_000L / STRIDE, test(0, 10_000_000), "loop not stopped"); + Asserts.assertEQ(-1L, test(0, Integer.MAX_VALUE), "loop stopped prematurely"); + } + + static long ONE = 1; // Just so the compiler doesn't try to IV replace the whole thing + + public static long test(int init, int limit) { + final int stride = 100_000; + + long iterations = 0; + for (int i = init; i < limit; i += 100000) { + iterations += ONE; + + if (iterations > (limit / stride) + 1) { // No it's not stopping, as we should expect. + return -1; + } + } + return iterations; // Possibly stopping prematurely. + } + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/UseCountedLoopSafepointsTest.java b/test/hotspot/jtreg/compiler/loopopts/UseCountedLoopSafepointsTest.java index 2a9cedfc87e..378c8e6da75 100644 --- a/test/hotspot/jtreg/compiler/loopopts/UseCountedLoopSafepointsTest.java +++ b/test/hotspot/jtreg/compiler/loopopts/UseCountedLoopSafepointsTest.java @@ -28,7 +28,7 @@ * @summary Test that C2 flag UseCountedLoopSafepoints ensures a safepoint is kept in a CountedLoop * @library /test/lib / * @requires vm.compMode != "Xint" & vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) & vm.debug == true - * @requires !vm.emulatedClient & !vm.graal.enabled + * @requires !vm.graal.enabled * @modules java.base/jdk.internal.misc * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox @@ -37,7 +37,6 @@ package compiler.loopopts; -import jdk.test.lib.Platform; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; import java.util.List; diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.java index 873533aaba8..e6edda9085a 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. 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 @@ -24,13 +25,15 @@ package compiler.loopopts.superword; import compiler.lib.ir_framework.*; +import compiler.lib.verify.Verify; import jdk.test.lib.Utils; import jdk.test.whitebox.WhiteBox; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; import java.lang.reflect.Array; import java.util.Map; import java.util.HashMap; import java.util.Random; -import java.nio.ByteOrder; /* * @test @@ -61,6 +64,8 @@ public class TestCompatibleUseDefTypeSize { float[] bF; double[] aD; double[] bD; + MemorySegment aMSF; + MemorySegment aMSD; // List of tests Map tests = new HashMap(); @@ -92,6 +97,8 @@ public class TestCompatibleUseDefTypeSize { bF = generateF(); aD = generateD(); bD = generateD(); + aMSF = generateMemorySegmentF(); + aMSD = generateMemorySegmentD(); // Add all tests to list tests.put("test0", () -> { return test0(aB.clone(), bC.clone()); }); @@ -122,6 +129,10 @@ public class TestCompatibleUseDefTypeSize { tests.put("testLongToShort", () -> { return testLongToShort(aL.clone(), bS.clone()); }); tests.put("testLongToChar", () -> { return testLongToChar(aL.clone(), bC.clone()); }); tests.put("testLongToInt", () -> { return testLongToInt(aL.clone(), bI.clone()); }); + tests.put("testFloatToIntMemorySegment", () -> { return testFloatToIntMemorySegment(copyF(aMSF), bF.clone()); }); + tests.put("testDoubleToLongMemorySegment", () -> { return testDoubleToLongMemorySegment(copyD(aMSD), bD.clone()); }); + tests.put("testIntToFloatMemorySegment", () -> { return testIntToFloatMemorySegment(copyF(aMSF), bF.clone()); }); + tests.put("testLongToDoubleMemorySegment", () -> { return testLongToDoubleMemorySegment(copyD(aMSD), bD.clone()); }); // Compute gold value for all test methods before compilation for (Map.Entry entry : tests.entrySet()) { @@ -160,7 +171,11 @@ public class TestCompatibleUseDefTypeSize { "testLongToByte", "testLongToShort", "testLongToChar", - "testLongToInt"}) + "testLongToInt", + "testFloatToIntMemorySegment", + "testDoubleToLongMemorySegment", + "testIntToFloatMemorySegment", + "testLongToDoubleMemorySegment"}) public void runTests() { for (Map.Entry entry : tests.entrySet()) { String name = entry.getKey(); @@ -170,7 +185,7 @@ public class TestCompatibleUseDefTypeSize { // Compute new result Object[] result = test.run(); // Compare gold and new result - verify(name, gold, result); + Verify.checkEQ(gold, result); } } @@ -230,119 +245,32 @@ public class TestCompatibleUseDefTypeSize { return a; } - static void verify(String name, Object[] gold, Object[] result) { - if (gold.length != result.length) { - throw new RuntimeException("verify " + name + ": not the same number of outputs: gold.length = " + - gold.length + ", result.length = " + result.length); - } - for (int i = 0; i < gold.length; i++) { - Object g = gold[i]; - Object r = result[i]; - if (g.getClass() != r.getClass() || !g.getClass().isArray() || !r.getClass().isArray()) { - throw new RuntimeException("verify " + name + ": must both be array of same type:" + - " gold[" + i + "].getClass() = " + g.getClass().getSimpleName() + - " result[" + i + "].getClass() = " + r.getClass().getSimpleName()); - } - if (g == r) { - throw new RuntimeException("verify " + name + ": should be two separate arrays (with identical content):" + - " gold[" + i + "] == result[" + i + "]"); - } - if (Array.getLength(g) != Array.getLength(r)) { - throw new RuntimeException("verify " + name + ": arrays must have same length:" + - " gold[" + i + "].length = " + Array.getLength(g) + - " result[" + i + "].length = " + Array.getLength(r)); - } - Class c = g.getClass().getComponentType(); - if (c == byte.class) { - verifyB(name, i, (byte[])g, (byte[])r); - } else if (c == short.class) { - verifyS(name, i, (short[])g, (short[])r); - } else if (c == char.class) { - verifyC(name, i, (char[])g, (char[])r); - } else if (c == int.class) { - verifyI(name, i, (int[])g, (int[])r); - } else if (c == long.class) { - verifyL(name, i, (long[])g, (long[])r); - } else if (c == float.class) { - verifyF(name, i, (float[])g, (float[])r); - } else if (c == double.class) { - verifyD(name, i, (double[])g, (double[])r); - } else { - throw new RuntimeException("verify " + name + ": array type not supported for verify:" + - " gold[" + i + "].getClass() = " + g.getClass().getSimpleName() + - " result[" + i + "].getClass() = " + r.getClass().getSimpleName()); - } + static MemorySegment generateMemorySegmentF() { + MemorySegment a = MemorySegment.ofArray(new float[RANGE]); + for (int i = 0; i < (int) a.byteSize(); i += 8) { + a.set(ValueLayout.JAVA_LONG_UNALIGNED, i, RANDOM.nextLong()); } + return a; } - static void verifyB(String name, int i, byte[] g, byte[] r) { - for (int j = 0; j < g.length; j++) { - if (g[j] != r[j]) { - throw new RuntimeException("verify " + name + ": arrays must have same content:" + - " gold[" + i + "][" + j + "] = " + g[j] + - " result[" + i + "][" + j + "] = " + r[j]); - } - } + MemorySegment copyF(MemorySegment src) { + MemorySegment dst = generateMemorySegmentF(); + MemorySegment.copy(src, 0, dst, 0, src.byteSize()); + return dst; } - static void verifyS(String name, int i, short[] g, short[] r) { - for (int j = 0; j < g.length; j++) { - if (g[j] != r[j]) { - throw new RuntimeException("verify " + name + ": arrays must have same content:" + - " gold[" + i + "][" + j + "] = " + g[j] + - " result[" + i + "][" + j + "] = " + r[j]); - } + static MemorySegment generateMemorySegmentD() { + MemorySegment a = MemorySegment.ofArray(new double[RANGE]); + for (int i = 0; i < (int) a.byteSize(); i += 8) { + a.set(ValueLayout.JAVA_LONG_UNALIGNED, i, RANDOM.nextLong()); } + return a; } - static void verifyC(String name, int i, char[] g, char[] r) { - for (int j = 0; j < g.length; j++) { - if (g[j] != r[j]) { - throw new RuntimeException("verify " + name + ": arrays must have same content:" + - " gold[" + i + "][" + j + "] = " + g[j] + - " result[" + i + "][" + j + "] = " + r[j]); - } - } - } - - static void verifyI(String name, int i, int[] g, int[] r) { - for (int j = 0; j < g.length; j++) { - if (g[j] != r[j]) { - throw new RuntimeException("verify " + name + ": arrays must have same content:" + - " gold[" + i + "][" + j + "] = " + g[j] + - " result[" + i + "][" + j + "] = " + r[j]); - } - } - } - - static void verifyL(String name, int i, long[] g, long[] r) { - for (int j = 0; j < g.length; j++) { - if (g[j] != r[j]) { - throw new RuntimeException("verify " + name + ": arrays must have same content:" + - " gold[" + i + "][" + j + "] = " + g[j] + - " result[" + i + "][" + j + "] = " + r[j]); - } - } - } - - static void verifyF(String name, int i, float[] g, float[] r) { - for (int j = 0; j < g.length; j++) { - if (Float.floatToIntBits(g[j]) != Float.floatToIntBits(r[j])) { - throw new RuntimeException("verify " + name + ": arrays must have same content:" + - " gold[" + i + "][" + j + "] = " + g[j] + - " result[" + i + "][" + j + "] = " + r[j]); - } - } - } - - static void verifyD(String name, int i, double[] g, double[] r) { - for (int j = 0; j < g.length; j++) { - if (Double.doubleToLongBits(g[j]) != Double.doubleToLongBits(r[j])) { - throw new RuntimeException("verify " + name + ": arrays must have same content:" + - " gold[" + i + "][" + j + "] = " + g[j] + - " result[" + i + "][" + j + "] = " + r[j]); - } - } + MemorySegment copyD(MemorySegment src) { + MemorySegment dst = generateMemorySegmentD(); + MemorySegment.copy(src, 0, dst, 0, src.byteSize()); + return dst; } @Test @@ -707,4 +635,64 @@ public class TestCompatibleUseDefTypeSize { return new Object[] { ints, res }; } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", "> 0", + IRNode.STORE_VECTOR, "> 0", + IRNode.VECTOR_REINTERPRET, "> 0"}, + applyIf = {"AlignVector", "false"}, + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) + static Object[] testFloatToIntMemorySegment(MemorySegment a, float[] b) { + for (int i = 0; i < RANGE; i++) { + a.set(ValueLayout.JAVA_FLOAT_UNALIGNED, 4L * i, b[i]); + } + + return new Object[]{ a, b }; + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", "> 0", + IRNode.STORE_VECTOR, "> 0", + IRNode.VECTOR_REINTERPRET, "> 0"}, + applyIf = {"AlignVector", "false"}, + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) + static Object[] testDoubleToLongMemorySegment(MemorySegment a, double[] b) { + for (int i = 0; i < RANGE; i++) { + a.set(ValueLayout.JAVA_DOUBLE_UNALIGNED, 8L * i, b[i]); + } + + return new Object[]{ a, b }; + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.STORE_VECTOR, "> 0", + IRNode.VECTOR_REINTERPRET, "> 0"}, + applyIf = {"AlignVector", "false"}, + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) + static Object[] testIntToFloatMemorySegment(MemorySegment a, float[] b) { + for (int i = 0; i < RANGE; i++) { + b[i] = a.get(ValueLayout.JAVA_FLOAT_UNALIGNED, 4L * i); + } + + return new Object[]{ a, b }; + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_L, "> 0", + IRNode.STORE_VECTOR, "> 0", + IRNode.VECTOR_REINTERPRET, "> 0"}, + applyIf = {"AlignVector", "false"}, + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) + static Object[] testLongToDoubleMemorySegment(MemorySegment a, double[] b) { + for (int i = 0; i < RANGE; i++) { + b[i] = a.get(ValueLayout.JAVA_DOUBLE_UNALIGNED, 8L * i); + } + + return new Object[]{ a, b }; + } } diff --git a/test/hotspot/jtreg/compiler/profiling/TestTypeProfiling.java b/test/hotspot/jtreg/compiler/profiling/TestTypeProfiling.java index 895589ea03f..9d64a08b691 100644 --- a/test/hotspot/jtreg/compiler/profiling/TestTypeProfiling.java +++ b/test/hotspot/jtreg/compiler/profiling/TestTypeProfiling.java @@ -27,7 +27,7 @@ * @summary Parameters type profiling is not performed from aarch64 interpreter * * @requires os.arch != "arm" - * @requires vm.flavor == "server" & vm.compMode == "Xmixed" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & vm.compMode == "Xmixed" & !vm.graal.enabled * * @comment the test can't be run w/ TieredStopAtLevel < 4 * @requires vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4 @@ -94,8 +94,8 @@ public class TestTypeProfiling { } static public void main(String[] args) throws Exception { - if (!Platform.isServer() || Platform.isEmulatedClient()) { - throw new Error("TESTBUG: Not server mode"); + if (!Platform.isServer()) { + throw new Error("TESTBUG: Not server VM"); } // Only execute if C2 is available if (TIERED_STOP_AT_LEVEL != CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) { diff --git a/test/hotspot/jtreg/compiler/rangechecks/TestExplicitRangeChecks.java b/test/hotspot/jtreg/compiler/rangechecks/TestExplicitRangeChecks.java index d74a10ae02c..c48609647c1 100644 --- a/test/hotspot/jtreg/compiler/rangechecks/TestExplicitRangeChecks.java +++ b/test/hotspot/jtreg/compiler/rangechecks/TestExplicitRangeChecks.java @@ -447,8 +447,7 @@ public class TestExplicitRangeChecks { success = false; } // Only perform these additional checks if C2 is available - if (Platform.isServer() && !Platform.isEmulatedClient() && - TIERED_STOP_AT_LEVEL == CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) { + if (Platform.isServer() && TIERED_STOP_AT_LEVEL == CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) { if (deoptimize && WHITE_BOX.isMethodCompiled(m)) { System.out.println(name + " not deoptimized on invalid access"); success = false; diff --git a/test/hotspot/jtreg/compiler/splitif/TestTraceSplitIf.java b/test/hotspot/jtreg/compiler/splitif/TestTraceSplitIf.java new file mode 100644 index 00000000000..32f3a213215 --- /dev/null +++ b/test/hotspot/jtreg/compiler/splitif/TestTraceSplitIf.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2026, 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 8351847 + * @summary Sanity test for -XX:+TraceSplitIf to ensure it doesn't crash and produces output. + * @library /test/lib + * @requires vm.compiler2.enabled & vm.debug + * @run driver ${test.main.class} + */ + +package compiler.splitif; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestTraceSplitIf { + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+TraceSplitIf", + "-Xcomp", + HelloWorld.class.getName() + ); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + output.shouldHaveExitValue(0); + output.shouldContain("Merging Identical Ifs"); + output.shouldContain("Split-If"); + } + + public static class HelloWorld { + public static void main(String[] args) { + System.out.println("Hello, World!"); + } + } +} diff --git a/test/hotspot/jtreg/compiler/stable/TestStableArrayMembars.java b/test/hotspot/jtreg/compiler/stable/TestStableArrayMembars.java new file mode 100644 index 00000000000..9d7babddfb9 --- /dev/null +++ b/test/hotspot/jtreg/compiler/stable/TestStableArrayMembars.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2026, 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 8377541 + * @summary Test that membars are eliminated when loading from a stable array. + * @library /test/lib / + * @modules java.base/jdk.internal.misc + * @modules java.base/jdk.internal.vm.annotation + * @run driver ${test.main.class} + */ + +package compiler.stable; + +import java.util.Objects; + +import jdk.internal.misc.Unsafe; +import jdk.internal.vm.annotation.Stable; + +import compiler.lib.ir_framework.*; + +public class TestStableArrayMembars { + + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + public static void main(String[] args) { + TestFramework tf = new TestFramework(); + tf.addTestClassesToBootClassPath(); + tf.addFlags( "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED"); + tf.start(); + } + + static final class LazyIntArray { + @Stable + private final Integer[] arr; + + LazyIntArray() { + this.arr = new Integer[10]; + } + + @ForceInline + Integer get(int idx) { + Integer i = contentsAcquire(offsetFor(idx)); + return i == null ? slowPath(arr, idx) : i; + } + + @ForceInline + private Integer contentsAcquire(long offset) { + return (Integer) UNSAFE.getReferenceAcquire(arr, offset); + } + + @ForceInline + private static long offsetFor(long index) { + return Unsafe.ARRAY_OBJECT_BASE_OFFSET + Unsafe.ARRAY_OBJECT_INDEX_SCALE * index; + } + + static Integer slowPath(final Integer[] array, final int index) { + final long offset = offsetFor(index); + final Integer t = array[index]; + if (t == null) { + final Integer newValue = Integer.valueOf(42); + Objects.requireNonNull(newValue); + set(array, index, newValue); + + return newValue; + } + return t; + } + + static void set(Integer[] array, int index, Integer newValue) { + if (array[index] == null) { + UNSAFE.putReferenceRelease(array, Unsafe.ARRAY_OBJECT_BASE_OFFSET + Unsafe.ARRAY_OBJECT_INDEX_SCALE * (long) index, newValue); + } + } + } + + static final LazyIntArray la = new LazyIntArray(); + + @Test + @IR(failOn = { IRNode.LOAD, IRNode.MEMBAR }) + static Integer test() { + return la.get(0); + } +} diff --git a/test/hotspot/jtreg/compiler/testlibrary/CompilerUtils.java b/test/hotspot/jtreg/compiler/testlibrary/CompilerUtils.java index 93be1971ca3..a8d953c5a30 100644 --- a/test/hotspot/jtreg/compiler/testlibrary/CompilerUtils.java +++ b/test/hotspot/jtreg/compiler/testlibrary/CompilerUtils.java @@ -53,10 +53,10 @@ public class CompilerUtils { "TieredStopAtLevel has value out of int capacity"); return IntStream.rangeClosed(1, maxLevel).toArray(); } else { - if (Platform.isServer() && !Platform.isEmulatedClient()) { + if (Platform.isServer()) { return new int[]{4}; } - if (Platform.isClient() || Platform.isMinimal() || Platform.isEmulatedClient()) { + if (Platform.isClient() || Platform.isMinimal()) { return new int[]{1}; } } diff --git a/test/hotspot/jtreg/compiler/testlibrary/sha/predicate/IntrinsicPredicates.java b/test/hotspot/jtreg/compiler/testlibrary/sha/predicate/IntrinsicPredicates.java index b088f58bb5b..52925fcf993 100644 --- a/test/hotspot/jtreg/compiler/testlibrary/sha/predicate/IntrinsicPredicates.java +++ b/test/hotspot/jtreg/compiler/testlibrary/sha/predicate/IntrinsicPredicates.java @@ -56,7 +56,7 @@ public class IntrinsicPredicates { "TieredStopAtLevel"); boolean maxLevelIsReachable = (tieredMaxLevel == IntrinsicPredicates.TIERED_MAX_LEVEL); - return Platform.isServer() && !Platform.isEmulatedClient() && (!isTiered || maxLevelIsReachable); + return Platform.isServer() && (!isTiered || maxLevelIsReachable); }; public static final BooleanSupplier MD5_INSTRUCTION_AVAILABLE diff --git a/test/hotspot/jtreg/compiler/tiered/NonTieredLevelsTest.java b/test/hotspot/jtreg/compiler/tiered/NonTieredLevelsTest.java index d23b13a65e4..fd55187a7ef 100644 --- a/test/hotspot/jtreg/compiler/tiered/NonTieredLevelsTest.java +++ b/test/hotspot/jtreg/compiler/tiered/NonTieredLevelsTest.java @@ -47,10 +47,10 @@ public class NonTieredLevelsTest extends CompLevelsTest { private static final int AVAILABLE_COMP_LEVEL; private static final IntPredicate IS_AVAILABLE_COMPLEVEL; static { - if (Platform.isServer() && !Platform.isEmulatedClient()) { + if (Platform.isServer()) { AVAILABLE_COMP_LEVEL = COMP_LEVEL_FULL_OPTIMIZATION; IS_AVAILABLE_COMPLEVEL = x -> x == COMP_LEVEL_FULL_OPTIMIZATION; - } else if (Platform.isClient() || Platform.isMinimal() || Platform.isEmulatedClient()) { + } else if (Platform.isClient() || Platform.isMinimal()) { AVAILABLE_COMP_LEVEL = COMP_LEVEL_SIMPLE; IS_AVAILABLE_COMPLEVEL = x -> x == COMP_LEVEL_SIMPLE; } else { diff --git a/test/hotspot/jtreg/compiler/types/correctness/CorrectnessTest.java b/test/hotspot/jtreg/compiler/types/correctness/CorrectnessTest.java index 895f8674ed5..08cbdb29aea 100644 --- a/test/hotspot/jtreg/compiler/types/correctness/CorrectnessTest.java +++ b/test/hotspot/jtreg/compiler/types/correctness/CorrectnessTest.java @@ -25,7 +25,7 @@ * @test CorrectnessTest * @bug 8038418 * @summary Tests correctness of type usage with type profiling and speculations - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management @@ -87,8 +87,8 @@ public class CorrectnessTest { private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); public static void main(String[] args) { - if (!Platform.isServer() || Platform.isEmulatedClient()) { - throw new Error("TESTBUG: Not server mode"); + if (!Platform.isServer()) { + throw new Error("TESTBUG: Not server VM"); } Asserts.assertGTE(args.length, 1); ProfilingType profilingType = ProfilingType.valueOf(args[0]); diff --git a/test/hotspot/jtreg/compiler/types/correctness/OffTest.java b/test/hotspot/jtreg/compiler/types/correctness/OffTest.java index 6f2354cf895..338c818fd3a 100644 --- a/test/hotspot/jtreg/compiler/types/correctness/OffTest.java +++ b/test/hotspot/jtreg/compiler/types/correctness/OffTest.java @@ -25,7 +25,7 @@ * @test CorrectnessTest * @key randomness * @bug 8038418 - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TestUnstableIfTrap.java b/test/hotspot/jtreg/compiler/uncommontrap/TestUnstableIfTrap.java index 04293370eb5..d539b56d850 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TestUnstableIfTrap.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TestUnstableIfTrap.java @@ -210,7 +210,7 @@ public class TestUnstableIfTrap { boolean isMethodCompiledAtMaxTier = WB.getMethodCompilationLevel(m) == MAX_TIER; - return Platform.isServer() && !Platform.isEmulatedClient() && isMethodCompiled + return Platform.isServer() && isMethodCompiled && (!isTiered || isMethodCompiledAtMaxTier); } diff --git a/test/hotspot/jtreg/compiler/unsafe/UnsafeGetConstantField.java b/test/hotspot/jtreg/compiler/unsafe/UnsafeGetConstantField.java index 8b20cbf8ecd..bab22bb0cd0 100644 --- a/test/hotspot/jtreg/compiler/unsafe/UnsafeGetConstantField.java +++ b/test/hotspot/jtreg/compiler/unsafe/UnsafeGetConstantField.java @@ -27,7 +27,7 @@ * @library /test/lib * @library /testlibrary/asm * - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * * @modules java.base/jdk.internal.vm.annotation * java.base/jdk.internal.misc @@ -97,8 +97,8 @@ public class UnsafeGetConstantField { static final Unsafe U = Unsafe.getUnsafe(); public static void main(String[] args) { - if (!Platform.isServer() || Platform.isEmulatedClient()) { - throw new Error("TESTBUG: Not server mode"); + if (!Platform.isServer()) { + throw new Error("TESTBUG: Not server VM"); } testUnsafeGetAddress(); testUnsafeGetField(); diff --git a/test/hotspot/jtreg/compiler/unsafe/UnsafeGetStableArrayElement.java b/test/hotspot/jtreg/compiler/unsafe/UnsafeGetStableArrayElement.java index 9d3578bd6a1..a124c58055a 100644 --- a/test/hotspot/jtreg/compiler/unsafe/UnsafeGetStableArrayElement.java +++ b/test/hotspot/jtreg/compiler/unsafe/UnsafeGetStableArrayElement.java @@ -26,7 +26,7 @@ * @summary tests on constant folding of unsafe get operations from stable arrays * @library /test/lib * @build jdk.test.whitebox.WhiteBox - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * * @modules java.base/jdk.internal.vm.annotation * java.base/jdk.internal.misc @@ -343,8 +343,8 @@ public class UnsafeGetStableArrayElement { } public static void main(String[] args) throws Exception { - if (!Platform.isServer() || Platform.isEmulatedClient()) { - throw new Error("TESTBUG: Not server mode"); + if (!Platform.isServer()) { + throw new Error("TESTBUG: Not server VM"); } testUnsafeAccess(); System.out.println("TEST PASSED"); diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestVectorLongToMaskNodeIdealization.java b/test/hotspot/jtreg/compiler/vectorapi/TestVectorLongToMaskNodeIdealization.java new file mode 100644 index 00000000000..706e21554bb --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestVectorLongToMaskNodeIdealization.java @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2026, 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.vectorapi; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.Random; + +import jdk.test.lib.Utils; + +import compiler.lib.ir_framework.*; +import compiler.lib.verify.Verify; +import jdk.incubator.vector.*; +import compiler.lib.compile_framework.CompileFramework; + +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateToken; +import compiler.lib.template_framework.library.TestFrameworkClass; +import compiler.lib.template_framework.library.PrimitiveType; +import compiler.lib.template_framework.library.CodeGenerationDataNameType; +import static compiler.lib.template_framework.Template.scope; +import static compiler.lib.template_framework.Template.let; + +/* + * @test + * @bug 8277997 8378968 + * @key randomness + * @summary Testing some optimizations in VectorLongToMaskNode::Ideal + * For now: VectorMask.fromLong(.., mask.toLong()) + * @modules jdk.incubator.vector + * @library /test/lib / + * @compile ../../compiler/lib/ir_framework/TestFramework.java + * @compile ../../compiler/lib/generators/Generators.java + * @compile ../../compiler/lib/verify/Verify.java + * @run driver ${test.main.class} + */ +public class TestVectorLongToMaskNodeIdealization { + private static final Random RANDOM = Utils.getRandomInstance(); + + static final long[] ONES_L = new long[64]; + static { Arrays.fill(ONES_L, 1); } + + static final boolean[] TRUES = new boolean[64]; + static { Arrays.fill(TRUES, true); } + + public static void main(String[] args) { + // Run some tests directly first. + TestFramework testFramework = new TestFramework(); + testFramework.setDefaultWarmup(10000) + .addFlags("--add-modules=jdk.incubator.vector") + .start(); + + // Then also generate some random examples. + CompileFramework comp = new CompileFramework(); + comp.addJavaSourceCode("compiler.vectorapi.templated.Templated", generate(comp)); + comp.compile("--add-modules=jdk.incubator.vector"); + comp.invoke("compiler.vectorapi.templated.Templated", "main", new Object[] {new String[] { + "--add-modules=jdk.incubator.vector", + "--add-opens", "jdk.incubator.vector/jdk.incubator.vector=ALL-UNNAMED", + "--add-opens", "java.base/java.lang=ALL-UNNAMED" + }}); + } + + // ------------------------------------------------------------------------------------- + @Test + @IR(counts = {IRNode.REPLICATE_L, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.REPLICATE_I, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.VECTOR_MASK_CMP, "> 0", + IRNode.VECTOR_LOAD_MASK, "> 0", // Not yet optimized away + IRNode.VECTOR_STORE_MASK, "> 0", // Not yet optimized away + IRNode.VECTOR_LONG_TO_MASK, "= 0", // Optimized away + IRNode.VECTOR_MASK_TO_LONG, "= 0", // Optimized away + IRNode.VECTOR_MASK_CAST, "> 0", // Not yet optimized away + IRNode.VECTOR_BLEND_I, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.XOR_VI, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureAnd = {"avx2", "true", "avx512", "false"}) + @IR(counts = {IRNode.REPLICATE_L, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.REPLICATE_I, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.VECTOR_MASK_CMP, "> 0", + IRNode.VECTOR_LOAD_MASK, "= 0", // Optimized away + IRNode.VECTOR_STORE_MASK, "= 0", // Optimized away + IRNode.VECTOR_LONG_TO_MASK, "= 0", // Optimized away + IRNode.VECTOR_MASK_TO_LONG, "= 0", // Optimized away + IRNode.VECTOR_MASK_CAST, "> 0", // Cast I->J + IRNode.VECTOR_BLEND_I, "= 0", // Not needed + IRNode.XOR_VI, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeature = {"avx512", "true"}) + // This is the original reproducer for JDK-8378968, which failed on AVX2 with a wrong result. + public static Object test1() { + // There was a bug here with AVX2: + var ones = LongVector.broadcast(LongVector.SPECIES_256, 1); + var trues_L256 = ones.compare(VectorOperators.NE, 0); + // VectorMaskCmp #vectory -> (L-mask=0x0..0/0xF..F) + + var trues_I128 = VectorMask.fromLong(IntVector.SPECIES_128, trues_L256.toLong()); + // VectorStoreMask(L-mask to 0/1) + // VectorMaskToLong + // AndL(truncate) + // VectorLongToMask -> 0/1 + // + // VectorLongToMaskNode::Ideal transforms this into: + // VectorMaskCmp #vectory -> (L-mask=0x0..0/0xF..F) + // VectorMaskCastNode -> (L-mask=0x0..0/0xF..F to 0/1) + // But VectorMaskCastNode is not made for such mask conversion to boolean mask, + // and so it wrongly produces a 0x00/0xFF byte mask, instead of bytes 0x00/01. + // See: vector_mask_cast + // + // The correct transformation would have been to: + // VectorMaskCmp #vectory -> (L-mask=0x0..0/0xF..F) + // VectorStoreMask(L-mask to 0/1, i.e. 0x00/0x01 bytes) + + var zeros = IntVector.zero(IntVector.SPECIES_128); + var m1s = zeros.lanewise(VectorOperators.NOT, trues_I128); + // The rest of the code is: + // VectorLoadMask (0/1 to I-mask=0x0..0/0xF..F) + // It expects x=0x00/0x01 bytes, and does a subtraction 0-x to get values 0x00/0xFF + // that are then widened to int-length. + // But if it instead gets 0x00/0xFF, the subtraction produces 0x00/0x01 values, which + // are widened to int 0x0..0/0..01 values. + // See: load_vector_mask + // Blend, which expects I-mask (0x0..0/0xF..F) + // It looks at the 7th (uppermost) bit of every byte to determine which byte is taken. + // If it instead gets the 0x0..0/0x0..01 mask, it interprets both as "false". + + int[] out = new int[64]; + m1s.intoArray(out, 0); + return out; + } + + static final Object GOLD_TEST1 = test1(); + + @Check(test = "test1") + public static void check_test1(Object out) { + Verify.checkEQ(GOLD_TEST1, out); + } + // ------------------------------------------------------------------------------------- + @Test + @IR(counts = {IRNode.REPLICATE_L, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.REPLICATE_I, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.LOAD_VECTOR_L, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.VECTOR_MASK_CMP, "> 0", + IRNode.VECTOR_LOAD_MASK, "> 0", // Not yet optimized away + IRNode.VECTOR_STORE_MASK, "> 0", // Not yet optimized away + IRNode.VECTOR_LONG_TO_MASK, "= 0", // Optimized away + IRNode.VECTOR_MASK_TO_LONG, "= 0", // Optimized away + IRNode.VECTOR_MASK_CAST, "> 0", // Not yet optimized away: Cast Z->Z, see JDK-8379866 + IRNode.VECTOR_BLEND_I, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.XOR_VI, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureAnd = {"avx2", "true", "avx512", "false"}) + @IR(counts = {IRNode.REPLICATE_L, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.REPLICATE_I, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.LOAD_VECTOR_L, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.VECTOR_MASK_CMP, "> 0", + IRNode.VECTOR_LOAD_MASK, "= 0", // Optimized away + IRNode.VECTOR_STORE_MASK, "= 0", // Optimized away + IRNode.VECTOR_LONG_TO_MASK, "= 0", // Optimized away + IRNode.VECTOR_MASK_TO_LONG, "= 0", // Optimized away + IRNode.VECTOR_MASK_CAST, "> 0", // Cast I->J + IRNode.VECTOR_BLEND_I, "= 0", // Not needed + IRNode.XOR_VI, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeature = {"avx512", "true"}) + // The original reproducer test1 could eventually constant-fold the comparison + // with zero and trues. So let's make sure we load the data from a mutable array. + public static Object test1b() { + // load instead of broadcast: + var ones = LongVector.fromArray(LongVector.SPECIES_256, ONES_L, 0); + var trues_L256 = ones.compare(VectorOperators.NE, 0); + + var trues_I128 = VectorMask.fromLong(IntVector.SPECIES_128, trues_L256.toLong()); + + var zeros = IntVector.zero(IntVector.SPECIES_128); + var m1s = zeros.lanewise(VectorOperators.NOT, trues_I128); + + int[] out = new int[64]; + m1s.intoArray(out, 0); + return out; + } + + static final Object GOLD_TEST1B = test1b(); + + @Check(test = "test1b") + public static void check_test1b(Object out) { + Verify.checkEQ(GOLD_TEST1B, out); + } + // ------------------------------------------------------------------------------------- + @Test + @IR(counts = {IRNode.REPLICATE_L, IRNode.VECTOR_SIZE_4, "= 0", + IRNode.REPLICATE_I, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.LOAD_VECTOR_Z, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.VECTOR_MASK_CMP, "= 0", + IRNode.VECTOR_LOAD_MASK, "> 0", + IRNode.VECTOR_STORE_MASK, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0", // Optimized away + IRNode.VECTOR_MASK_TO_LONG, "= 0", // Optimized away + IRNode.VECTOR_MASK_CAST, "> 0", // Not yet optimized Z->Z, see JDK-8379866 + IRNode.VECTOR_BLEND_I, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.XOR_VI, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureAnd = {"avx2", "true", "avx512", "false"}) + @IR(counts = {IRNode.REPLICATE_L, IRNode.VECTOR_SIZE_4, "= 0", + IRNode.REPLICATE_I, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.LOAD_VECTOR_Z, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.VECTOR_MASK_CMP, "= 0", + IRNode.VECTOR_LOAD_MASK, "> 0", + IRNode.VECTOR_STORE_MASK, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0", // Optimized away + IRNode.VECTOR_MASK_TO_LONG, "= 0", // Optimized away + IRNode.VECTOR_MASK_CAST, "> 0", // Cast I->J + IRNode.VECTOR_BLEND_I, "= 0", // Not needed + IRNode.XOR_VI, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeature = {"avx512", "true"}) + // And now let's try a case where we load the mask from boolean array, so we don't + // have the VectorStoreMask before the VectorMaskToLong. + public static Object test1c() { + // Load true mask from array directly. + var trues_L256 = VectorMask.fromArray(LongVector.SPECIES_256, TRUES, 0); + + var trues_I128 = VectorMask.fromLong(IntVector.SPECIES_128, trues_L256.toLong()); + + var zeros = IntVector.zero(IntVector.SPECIES_128); + var m1s = zeros.lanewise(VectorOperators.NOT, trues_I128); + + int[] out = new int[64]; + m1s.intoArray(out, 0); + return out; + } + + static final Object GOLD_TEST1C = test1c(); + + @Check(test = "test1c") + public static void check_test1c(Object out) { + Verify.checkEQ(GOLD_TEST1C, out); + } + // ------------------------------------------------------------------------------------- + + // TODO: we can refactor this away once JDK-8369699 is integrated. + record VectorType(PrimitiveType elementType, int length) { + String typeName() { + return switch(elementType.name()) { + case "byte" -> "ByteVector"; + case "short" -> "ShortVector"; + case "int" -> "IntVector"; + case "long" -> "LongVector"; + case "float" -> "FloatVector"; + case "double" -> "DoubleVector"; + default -> throw new UnsupportedOperationException("Not supported: " + elementType.name()); + }; + } + + String speciesName() { + return typeName() + ".SPECIES_" + bitSize(); + } + + int bitSize() { + return elementType.byteSize() * length() * 8; + } + } + + public static final List VECTOR_TYPES = List.of( + new VectorType(CodeGenerationDataNameType.bytes(), 8), + new VectorType(CodeGenerationDataNameType.bytes(), 16), + new VectorType(CodeGenerationDataNameType.bytes(), 32), + new VectorType(CodeGenerationDataNameType.bytes(), 64), + new VectorType(CodeGenerationDataNameType.shorts(), 4), + new VectorType(CodeGenerationDataNameType.shorts(), 8), + new VectorType(CodeGenerationDataNameType.shorts(), 16), + new VectorType(CodeGenerationDataNameType.shorts(), 32), + new VectorType(CodeGenerationDataNameType.ints(), 2), + new VectorType(CodeGenerationDataNameType.ints(), 4), + new VectorType(CodeGenerationDataNameType.ints(), 8), + new VectorType(CodeGenerationDataNameType.ints(), 16), + new VectorType(CodeGenerationDataNameType.longs(), 1), + new VectorType(CodeGenerationDataNameType.longs(), 2), + new VectorType(CodeGenerationDataNameType.longs(), 4), + new VectorType(CodeGenerationDataNameType.longs(), 8), + new VectorType(CodeGenerationDataNameType.floats(), 2), + new VectorType(CodeGenerationDataNameType.floats(), 4), + new VectorType(CodeGenerationDataNameType.floats(), 8), + new VectorType(CodeGenerationDataNameType.floats(), 16), + new VectorType(CodeGenerationDataNameType.doubles(), 1), + new VectorType(CodeGenerationDataNameType.doubles(), 2), + new VectorType(CodeGenerationDataNameType.doubles(), 4), + new VectorType(CodeGenerationDataNameType.doubles(), 8) + ); + + private static String generate(CompileFramework comp) { + // Create a list to collect all tests. + List tests = new ArrayList<>(); + + var testTemplate = Template.make("t1", "t2", (VectorType t1, VectorType t2) -> scope( + let("e1", t1.elementType()), + let("e2", t2.elementType()), + let("V1", t1.typeName()), + let("V2", t2.typeName()), + let("S1", t1.speciesName()), + let("S2", t2.speciesName()), + """ + // ------------ $test ------------- + @Test + @Warmup(10_000) + """, + // Now let's generate some IR rules. + // General Idea: if the lengths match, we can optimize. If the don't match, it is + // a truncation/zero extension, and we don't optimize. + // + // TODO: length=64 leads the AndL mask to be all-ones, and fold away immediately. + // We could eventually extend the optimization to handle that. See JDK-8379398. + // + // AVX512: expect vectorization in length range [4..64] + // TODO: 2-element masks are currently not properly intrinsified, see JDK-8378589. + (t1.length() >= 4 && t2.length() >= 4) + ?( (t1.length() == t2.length && t1.length() != 64) + ? """ + @IR(counts = {IRNode.VECTOR_LONG_TO_MASK, "= 0", // Optimized away + IRNode.VECTOR_MASK_TO_LONG, "= 0"}, // Optimized away + applyIfCPUFeature = {"avx512", "true"}) + """ + : """ + @IR(counts = {IRNode.VECTOR_LONG_TO_MASK, "> 0", // Cannot optimize + IRNode.VECTOR_MASK_TO_LONG, "> 0"}, // Cannot optimize + applyIfCPUFeature = {"avx512", "true"}) + """) + :( """ + // AVX512: at least one vector length not in range [4..64] -> no IR rule. + """), + // AVX2: expect vectorization if: length >= 4 and bitSize <= 256 + // TODO: 2-element masks are currently not properly intrinsified, see JDK-8378589. + (t1.bitSize() <= 256 && t2.bitSize() <= 256 && t1.length() >= 4 && t2.length() >= 4) + ?( (t1.length() == t2.length) + ? """ + @IR(counts = {IRNode.VECTOR_LONG_TO_MASK, "= 0", // Optimized away + IRNode.VECTOR_MASK_TO_LONG, "= 0"}, // Optimized away + applyIfCPUFeatureAnd = {"avx2", "true", "avx512", "false"}) + """ + : """ + @IR(counts = {IRNode.VECTOR_LONG_TO_MASK, "> 0", // Cannot optimize + IRNode.VECTOR_MASK_TO_LONG, "> 0"}, // Cannot optimize + applyIfCPUFeatureAnd = {"avx2", "true", "avx512", "false"}) + """) + :( """ + // AVX2: at least one vector length not: length >= 4 and bitSize <= 256 -> no IR rule. + """), + """ + public static Object $test() { + var inputs = #V1.fromArray(#S1, $INPUT, 0); + var mask1 = inputs.compare(VectorOperators.GT, 0); + + var mask2 = VectorMask.fromLong(#S2, mask1.toLong()); + + var zeros = #V2.zero(#S2); + var m1s = #V2.broadcast(#S2, -1); + var res = zeros.blend(m1s, mask2); + + #e2[] out = new #e2[64]; + res.intoArray(out, 0); + return out; + } + + public static #e1[] $INPUT = new #e1[64]; + """, + "static { for (int i = 0; i < $INPUT.length; i++) { $INPUT[i] = ", t1.elementType().callLibraryRNG(), "; } }", + """ + public static Object $GOLD = $test(); + + @Check(test = "$test") + public static void $check(Object val) { + Verify.checkEQ($GOLD, val); + } + """ + )); + + tests.add(PrimitiveType.generateLibraryRNG()); + + // It would take a bit long to cover all 20*20=400 combinations, but we can sample some: + for (int i = 0; i < 20; i++) { + VectorType t1 = VECTOR_TYPES.get(RANDOM.nextInt(VECTOR_TYPES.size())); + VectorType t2 = VECTOR_TYPES.get(RANDOM.nextInt(VECTOR_TYPES.size())); + tests.add(testTemplate.asToken(t1, t2)); + } + + // Create the test class, which runs all testTemplateTokens. + return TestFrameworkClass.render( + // package and class name. + "compiler.vectorapi.templated", "Templated", + // List of imports. + Set.of("compiler.lib.verify.*", + "java.util.Random", + "jdk.test.lib.Utils", + "compiler.lib.generators.*", + "jdk.incubator.vector.*"), + // classpath, so the Test VM has access to the compiled class files. + comp.getEscapedClassPathOfCompiledClasses(), + // The list of tests. + tests); + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastIdentityTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastIdentityTest.java index be9d8e6390c..e4f166f510a 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastIdentityTest.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastIdentityTest.java @@ -50,7 +50,7 @@ public class VectorMaskCastIdentityTest { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "= 2" }, applyIfCPUFeatureOr = {"asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "= 2" }, applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public static int testTwoCastToDifferentType() { // The types before and after the two casts are not the same, so the cast cannot be eliminated. VectorMask mFloat64 = VectorMask.fromArray(FloatVector.SPECIES_64, mr, 0); @@ -84,7 +84,7 @@ public class VectorMaskCastIdentityTest { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "= 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "= 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public static int testTwoCastToSameType() { // The types before and after the two casts are the same, so the cast will be eliminated. VectorMask mInt128 = VectorMask.fromArray(IntVector.SPECIES_128, mr, 0); @@ -101,7 +101,7 @@ public class VectorMaskCastIdentityTest { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "= 1" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "= 1" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public static int testOneCastToDifferentType() { // The types before and after the only cast are different, the cast will not be eliminated. VectorMask mFloat128 = VectorMask.fromArray(FloatVector.SPECIES_128, mr, 0).not(); diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastTest.java index 25594d5caf9..1f346cffd2f 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastTest.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastTest.java @@ -74,7 +74,7 @@ public class VectorMaskCastTest { // Byte @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public static VectorMask testByte64ToShort128(VectorMask v) { return v.cast(ShortVector.SPECIES_128); } @@ -201,7 +201,7 @@ public class VectorMaskCastTest { // Short @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public static VectorMask testShort64ToInt128(VectorMask v) { return v.cast(IntVector.SPECIES_128); } @@ -215,7 +215,7 @@ public class VectorMaskCastTest { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public static VectorMask testShort64ToFloat128(VectorMask v) { return v.cast(FloatVector.SPECIES_128); } @@ -257,7 +257,7 @@ public class VectorMaskCastTest { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public static VectorMask testShort128ToByte64(VectorMask v) { return v.cast(ByteVector.SPECIES_64); } @@ -384,7 +384,7 @@ public class VectorMaskCastTest { // Int @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public static VectorMask testInt64ToLong128(VectorMask v) { return v.cast(LongVector.SPECIES_128); } @@ -398,7 +398,7 @@ public class VectorMaskCastTest { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public static VectorMask testInt64ToDouble128(VectorMask v) { return v.cast(DoubleVector.SPECIES_128); } @@ -412,7 +412,7 @@ public class VectorMaskCastTest { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public static VectorMask testInt128ToShort64(VectorMask v) { return v.cast(ShortVector.SPECIES_64); } @@ -539,7 +539,7 @@ public class VectorMaskCastTest { // Float @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public static VectorMask testFloat64ToLong128(VectorMask v) { return v.cast(LongVector.SPECIES_128); } @@ -553,7 +553,7 @@ public class VectorMaskCastTest { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public static VectorMask testFloat64ToDouble128(VectorMask v) { return v.cast(DoubleVector.SPECIES_128); } @@ -567,7 +567,7 @@ public class VectorMaskCastTest { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public static VectorMask testFloat128ToShort64(VectorMask v) { return v.cast(ShortVector.SPECIES_64); } @@ -694,7 +694,7 @@ public class VectorMaskCastTest { // Long @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public static VectorMask testLong128ToInt64(VectorMask v) { return v.cast(IntVector.SPECIES_64); } @@ -708,7 +708,7 @@ public class VectorMaskCastTest { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public static VectorMask testLong128ToFloat64(VectorMask v) { return v.cast(FloatVector.SPECIES_64); } @@ -821,7 +821,7 @@ public class VectorMaskCastTest { // Double @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public static VectorMask testDouble128ToInt64(VectorMask v) { return v.cast(IntVector.SPECIES_64); } @@ -835,7 +835,7 @@ public class VectorMaskCastTest { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public static VectorMask testDouble128ToFloat64(VectorMask v) { return v.cast(FloatVector.SPECIES_64); } diff --git a/test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java b/test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java index ed04950eb07..2667ac59471 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java @@ -262,11 +262,11 @@ public class TestVectorAlgorithms { @Test @IR(counts = {IRNode.REPLICATE_I, "= 1", IRNode.STORE_VECTOR, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIfAnd = {"UseSuperWord", "true", "OptimizeFill", "false"}) @IR(counts = {".*CallLeafNoFP.*jint_fill.*", "= 1"}, phase = CompilePhase.BEFORE_MATCHING, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"OptimizeFill", "true"}) // By default, the fill intrinsic "jint_fill" is used, but we can disable // the detection of the fill loop, and then we auto vectorize. @@ -277,7 +277,7 @@ public class TestVectorAlgorithms { @Test @IR(counts = {IRNode.REPLICATE_I, "= 1", IRNode.STORE_VECTOR, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) public Object fillI_VectorAPI(int[] r) { return VectorAlgorithmsImpl.fillI_VectorAPI(r); } @@ -292,7 +292,7 @@ public class TestVectorAlgorithms { @Test @IR(counts = {IRNode.POPULATE_INDEX, "> 0", IRNode.STORE_VECTOR, "> 0"}, - applyIfCPUFeatureOr = {"avx2", "true", "sve", "true"}, + applyIfCPUFeatureOr = {"avx2", "true", "sve", "true", "rvv", "true"}, applyIf = {"UseSuperWord", "true"}) // Note: the Vector API example below can also vectorize for AVX, // because it does not use a PopulateIndex. @@ -303,7 +303,7 @@ public class TestVectorAlgorithms { @Test @IR(counts = {IRNode.ADD_VI, "> 0", IRNode.STORE_VECTOR, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"TieredCompilation", "true"}) // IR check only works with TieredCompilation. Needs to make it // work with -XX:-TieredCompilation in future (see JDK-8378640). @@ -314,7 +314,7 @@ public class TestVectorAlgorithms { @Test @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.STORE_VECTOR, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"UseSuperWord", "true"}) public Object copyI_loop(int[] a, int[] r) { return VectorAlgorithmsImpl.copyI_loop(a, r); @@ -323,7 +323,7 @@ public class TestVectorAlgorithms { @Test @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.STORE_VECTOR, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) public Object copyI_VectorAPI(int[] a, int[] r) { return VectorAlgorithmsImpl.copyI_VectorAPI(a, r); } @@ -331,7 +331,7 @@ public class TestVectorAlgorithms { @Test @IR(counts = {".*CallLeafNoFP.*jint_disjoint_arraycopy.*", "= 1"}, phase = CompilePhase.BEFORE_MATCHING, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) public Object copyI_System_arraycopy(int[] a, int[] r) { return VectorAlgorithmsImpl.copyI_System_arraycopy(a, r); } @@ -340,7 +340,7 @@ public class TestVectorAlgorithms { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.MUL_VI, "> 0", IRNode.STORE_VECTOR, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"UseSuperWord", "true"}) public Object mapI_loop(int[] a, int[] r) { return VectorAlgorithmsImpl.mapI_loop(a, r); @@ -350,7 +350,7 @@ public class TestVectorAlgorithms { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.MUL_VI, "> 0", IRNode.STORE_VECTOR, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) public Object mapI_VectorAPI(int[] a, int[] r) { return VectorAlgorithmsImpl.mapI_VectorAPI(a, r); } @@ -359,7 +359,7 @@ public class TestVectorAlgorithms { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.ADD_REDUCTION_VI, "> 0", IRNode.ADD_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"UseSuperWord", "true"}) public int reduceAddI_loop(int[] a) { return VectorAlgorithmsImpl.reduceAddI_loop(a); @@ -373,7 +373,7 @@ public class TestVectorAlgorithms { @Test @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.ADD_REDUCTION_VI, "> 0"}, // reduceLanes inside loop - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) public int reduceAddI_VectorAPI_naive(int[] a) { return VectorAlgorithmsImpl.reduceAddI_VectorAPI_naive(a); } @@ -393,7 +393,7 @@ public class TestVectorAlgorithms { @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", IRNode.ADD_REDUCTION_V, "> 0", IRNode.MUL_VF, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"UseSuperWord", "true"}) public float dotProductF_VectorAPI_naive(float[] a, float[] b) { return VectorAlgorithmsImpl.dotProductF_VectorAPI_naive(a, b); @@ -403,7 +403,7 @@ public class TestVectorAlgorithms { @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", IRNode.ADD_REDUCTION_V, "> 0", IRNode.MUL_VF, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"UseSuperWord", "true"}) public float dotProductF_VectorAPI_reduction_after_loop(float[] a, float[] b) { return VectorAlgorithmsImpl.dotProductF_VectorAPI_reduction_after_loop(a, b); @@ -425,7 +425,7 @@ public class TestVectorAlgorithms { IRNode.MUL_VI, IRNode.VECTOR_SIZE_8, "> 0", IRNode.ADD_VI, IRNode.VECTOR_SIZE_8, "> 0", IRNode.ADD_REDUCTION_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx2", "true", "sve", "true"}, + applyIfCPUFeatureOr = {"avx2", "true", "sve", "true", "rvv", "true"}, applyIf = {"MaxVectorSize", ">=32"}) public int hashCodeB_VectorAPI_v1(byte[] a) { return VectorAlgorithmsImpl.hashCodeB_VectorAPI_v1(a); @@ -436,7 +436,7 @@ public class TestVectorAlgorithms { IRNode.MUL_VI, "> 0", IRNode.ADD_VI, "> 0", IRNode.ADD_REDUCTION_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public int hashCodeB_VectorAPI_v2(byte[] a) { return VectorAlgorithmsImpl.hashCodeB_VectorAPI_v2(a); } @@ -445,7 +445,7 @@ public class TestVectorAlgorithms { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.ADD_REDUCTION_VI, "> 0", IRNode.ADD_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) public int reduceAddI_VectorAPI_reduction_after_loop(int[] a) { return VectorAlgorithmsImpl.reduceAddI_VectorAPI_reduction_after_loop(a); } @@ -469,7 +469,7 @@ public class TestVectorAlgorithms { IRNode.AND_VI, "> 0", IRNode.ADD_VI, "> 0", IRNode.STORE_VECTOR, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"MaxVectorSize", ">=64"}) public Object scanAddI_VectorAPI_permute_add(int[] a, int[] r) { return VectorAlgorithmsImpl.scanAddI_VectorAPI_permute_add(a, r); @@ -488,7 +488,7 @@ public class TestVectorAlgorithms { IRNode.VECTOR_BLEND_I, "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.ADD_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) public int findMinIndexI_VectorAPI(int[] a) { return VectorAlgorithmsImpl.findMinIndexI_VectorAPI(a); } @@ -504,7 +504,7 @@ public class TestVectorAlgorithms { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.VECTOR_MASK_CMP, "> 0", IRNode.VECTOR_TEST, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) public int findI_VectorAPI(int[] a, int e) { return VectorAlgorithmsImpl.findI_VectorAPI(a, e); } @@ -522,7 +522,7 @@ public class TestVectorAlgorithms { IRNode.REARRANGE_VI, "> 0", IRNode.AND_VI, "> 0", IRNode.STORE_VECTOR, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) public Object reverseI_VectorAPI(int[] a, int[] r) { return VectorAlgorithmsImpl.reverseI_VectorAPI(a, r); } @@ -540,7 +540,7 @@ public class TestVectorAlgorithms { IRNode.VECTOR_TEST, "> 0", IRNode.COMPRESS_VI, "> 0", IRNode.STORE_VECTOR_MASKED, "> 0"}, - applyIfCPUFeatureOr = {"avx2", "true", "sve", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "sve", "true", "rvv", "true"}) public Object filterI_VectorAPI_v1(int[] a, int[] r, int threshold) { return VectorAlgorithmsImpl.filterI_VectorAPI_v1(a, r, threshold); } @@ -556,7 +556,7 @@ public class TestVectorAlgorithms { IRNode.VECTOR_MASK_CMP, "> 0", IRNode.VECTOR_TEST, "> 0", IRNode.STORE_VECTOR, "> 0"}, - applyIfCPUFeatureOr = {"asimd", "true"}) + applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public Object filterI_VectorAPI_v2_l2(int[] a, int[] r, int threshold) { return VectorAlgorithmsImpl.filterI_VectorAPI_v2_l2(a, r, threshold); } @@ -566,7 +566,7 @@ public class TestVectorAlgorithms { IRNode.VECTOR_MASK_CMP, "> 0", IRNode.VECTOR_TEST, "> 0", IRNode.STORE_VECTOR, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) public Object filterI_VectorAPI_v2_l4(int[] a, int[] r, int threshold) { return VectorAlgorithmsImpl.filterI_VectorAPI_v2_l4(a, r, threshold); } @@ -597,7 +597,7 @@ public class TestVectorAlgorithms { IRNode.OR_V_MASK, "> 0", IRNode.ADD_VI, "> 0", IRNode.ADD_REDUCTION_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx512", "true", "sve", "true"}, + applyIfCPUFeatureOr = {"avx512", "true", "sve", "true", "rvv", "true"}, applyIf = {"TieredCompilation", "true"}) // IR check only works with TieredCompilation. Needs to make it // work with -XX:-TieredCompilation in future (see JDK-8378640). @@ -615,7 +615,7 @@ public class TestVectorAlgorithms { @Test @IR(counts = {IRNode.LOAD_VECTOR_B, "> 0", IRNode.ADD_VB, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) public Object lowerCaseB_VectorAPI_v1(byte[] a, byte[] r) { return VectorAlgorithmsImpl.lowerCaseB_VectorAPI_v1(a, r); } @@ -623,7 +623,7 @@ public class TestVectorAlgorithms { @Test @IR(counts = {IRNode.LOAD_VECTOR_B, "> 0", IRNode.ADD_VB, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) public Object lowerCaseB_VectorAPI_v2(byte[] a, byte[] r) { return VectorAlgorithmsImpl.lowerCaseB_VectorAPI_v2(a, r); } @@ -647,7 +647,7 @@ public class TestVectorAlgorithms { @Test @IR(counts = {IRNode.LOAD_VECTOR_B, "> 0", IRNode.ADD_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) public Object conditionalSumB_VectorAPI_v2(byte[] a) { return VectorAlgorithmsImpl.conditionalSumB_VectorAPI_v2(a); } @@ -663,7 +663,7 @@ public class TestVectorAlgorithms { @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", IRNode.MUL_VF, "> 0", IRNode.SQRT_VF, "> 0"}, - applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public Object pieceWise2FunctionF_VectorAPI_v1(float[] a, float[] r) { return VectorAlgorithmsImpl.pieceWise2FunctionF_VectorAPI_v1(a, r); } @@ -672,7 +672,7 @@ public class TestVectorAlgorithms { @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", IRNode.MUL_VF, "> 0", IRNode.SQRT_VF, "> 0"}, - applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public Object pieceWise2FunctionF_VectorAPI_v2(float[] a, float[] r) { return VectorAlgorithmsImpl.pieceWise2FunctionF_VectorAPI_v2(a, r); } diff --git a/test/hotspot/jtreg/compiler/whitebox/DeoptimizeRelocatedNMethod.java b/test/hotspot/jtreg/compiler/whitebox/DeoptimizeRelocatedNMethod.java index c7cf355259b..672e24ad874 100644 --- a/test/hotspot/jtreg/compiler/whitebox/DeoptimizeRelocatedNMethod.java +++ b/test/hotspot/jtreg/compiler/whitebox/DeoptimizeRelocatedNMethod.java @@ -29,7 +29,6 @@ * @modules java.base/jdk.internal.misc java.management * @requires vm.opt.DeoptimizeALot != true * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) - * @requires !vm.emulatedClient * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache diff --git a/test/hotspot/jtreg/compiler/whitebox/IsMethodCompilableTest.java b/test/hotspot/jtreg/compiler/whitebox/IsMethodCompilableTest.java index 19f577067e7..fff6517fc34 100644 --- a/test/hotspot/jtreg/compiler/whitebox/IsMethodCompilableTest.java +++ b/test/hotspot/jtreg/compiler/whitebox/IsMethodCompilableTest.java @@ -31,7 +31,6 @@ * * @requires vm.opt.DeoptimizeALot != true * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) - * @requires !vm.emulatedClient * * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox @@ -84,8 +83,8 @@ public class IsMethodCompilableTest extends CompilerWhiteBoxTest { protected void test() throws Exception { // Only c2 compilations can be disabled through PerMethodRecompilationCutoff - if (!Platform.isServer() || Platform.isEmulatedClient()) { - throw new Error("TESTBUG: Not server mode"); + if (!Platform.isServer()) { + throw new Error("TESTBUG: Not server VM"); } if (skipXcompOSR()) { diff --git a/test/hotspot/jtreg/containers/docker/TestJcmd.java b/test/hotspot/jtreg/containers/docker/TestJcmd.java index fcd5c665f2b..4f6fda20c86 100644 --- a/test/hotspot/jtreg/containers/docker/TestJcmd.java +++ b/test/hotspot/jtreg/containers/docker/TestJcmd.java @@ -170,14 +170,10 @@ public class TestJcmd { opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z") .addJavaOpts("-cp", "/test-classes/") .addDockerOpts("--cap-add=SYS_PTRACE") + .addDockerOpts("--pull=never") .addDockerOpts("--name", CONTAINER_NAME) .addClassOptions("" + TIME_TO_RUN_CONTAINER_PROCESS); - if (IS_PODMAN && !ROOT_UID.equals(getId("-u"))) { - // map the current userid to the one in the target namespace - opts.addDockerOpts("--userns=keep-id"); - } - // avoid large Xmx opts.appendTestJavaOptions = false; @@ -186,7 +182,7 @@ public class TestJcmd { return ProcessTools.startProcess("main-container-process", pb, line -> line.contains(EventGeneratorLoop.MAIN_METHOD_STARTED), - 5, TimeUnit.SECONDS); + 15, TimeUnit.SECONDS); } diff --git a/test/hotspot/jtreg/gc/arguments/TestSelectDefaultGC.java b/test/hotspot/jtreg/gc/arguments/TestSelectDefaultGC.java deleted file mode 100644 index a5e144bcd15..00000000000 --- a/test/hotspot/jtreg/gc/arguments/TestSelectDefaultGC.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2015, 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. - * - * 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 gc.arguments; - -/* - * @test TestSelectDefaultGC - * @summary Test selection of GC when no GC option is specified - * @bug 8068582 - * @library /test/lib - * @library / - * @requires vm.gc.Serial & vm.gc.G1 - * @modules java.base/jdk.internal.misc - * java.management - * @run driver gc.arguments.TestSelectDefaultGC - */ - -import jdk.test.lib.process.OutputAnalyzer; - -public class TestSelectDefaultGC { - public static void assertVMOption(OutputAnalyzer output, String option, boolean value) { - output.shouldMatch(" " + option + " .*=.* " + value + " "); - } - - public static void testDefaultGC(boolean actAsServer) throws Exception { - // Start VM without specifying GC - OutputAnalyzer output = GCArguments.executeTestJava( - "-XX:" + (actAsServer ? "+" : "-") + "AlwaysActAsServerClassMachine", - "-XX:" + (actAsServer ? "-" : "+") + "NeverActAsServerClassMachine", - "-XX:+PrintFlagsFinal", - "-version"); - output.shouldHaveExitValue(0); - - final boolean isServer = actAsServer; - - // Verify GC selection - // G1 is default for server class machines - assertVMOption(output, "UseG1GC", isServer); - // Serial is default for non-server class machines - assertVMOption(output, "UseSerialGC", !isServer); - } - - public static void main(String[] args) throws Exception { - // Test server class machine - testDefaultGC(false); - - // Test non-server class machine - testDefaultGC(true); - } -} diff --git a/test/hotspot/jtreg/gc/ergonomics/TestInitialGCThreadLogging.java b/test/hotspot/jtreg/gc/ergonomics/TestInitialGCThreadLogging.java index 69a3e7b8742..52aa1bc26e5 100644 --- a/test/hotspot/jtreg/gc/ergonomics/TestInitialGCThreadLogging.java +++ b/test/hotspot/jtreg/gc/ergonomics/TestInitialGCThreadLogging.java @@ -55,7 +55,7 @@ public class TestInitialGCThreadLogging { if (GC.Shenandoah.isSupported()) { noneGCSupported = false; - testInitialGCThreadLogging("UseShenandoahGC", "Shenandoah GC Thread"); + testInitialGCThreadLogging("UseShenandoahGC", "ShenWorker"); } if (noneGCSupported) { diff --git a/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.java b/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.java index fd1924b9644..5637e578e8f 100644 --- a/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.java +++ b/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.java @@ -84,26 +84,19 @@ public class TestEagerReclaimHumongousRegions { * @param phase The phase during concurrent mark to reach before triggering a young garbage collection. * @return Returns the stdout of the VM. */ - private static String runHelperVM(ObjectType type, ReferencePolicy refPolicy, AllocationTiming timing, String phase) throws Exception { + private static String runHelperVM(List args, ObjectType type, ReferencePolicy refPolicy, AllocationTiming timing, String phase) throws Exception { boolean useTypeArray = (type == ObjectType.TYPE_ARRAY); boolean keepReference = (refPolicy == ReferencePolicy.KEEP); boolean allocateAfter = (timing == AllocationTiming.AFTER_MARK_START); - OutputAnalyzer output = ProcessTools.executeLimitedTestJava("-XX:+UseG1GC", - "-Xmx20M", - "-Xms20m", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+VerifyAfterGC", - "-Xbootclasspath/a:.", - "-Xlog:gc=debug,gc+humongous=debug", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+WhiteBoxAPI", - TestEagerReclaimHumongousRegionsClearMarkBitsRunner.class.getName(), - String.valueOf(useTypeArray), - String.valueOf(keepReference), - String.valueOf(allocateAfter), - phase); + args.add(TestEagerReclaimHumongousRegionsClearMarkBitsRunner.class.getName()); + args.add(String.valueOf(useTypeArray)); + args.add(String.valueOf(keepReference)); + args.add(String.valueOf(allocateAfter)); + args.add(phase); + + OutputAnalyzer output = ProcessTools.executeLimitedTestJava(args); String log = output.getStdout(); System.out.println(log); @@ -111,19 +104,25 @@ public class TestEagerReclaimHumongousRegions { return log; } + private static List testArgs() throws Exception { + return List.of("-XX:+UseG1GC", + "-Xmx20M", + "-Xms20m", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+VerifyAfterGC", + "-Xbootclasspath/a:.", + "-Xlog:gc=debug,gc+humongous=debug", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI"); + } + private static String boolToInt(boolean value) { return value ? "1" : "0"; } - private static void runTest(ObjectType type, - ReferencePolicy refPolicy, - AllocationTiming timing, - String phase, - ExpectedState expected) throws Exception { - String log = runHelperVM(type, refPolicy, timing, phase); - + private static void verifyLog(String log, AllocationTiming timing, ExpectedState expected) { // Find the log output indicating that the humongous object has been reclaimed, and marked and verify for the expected results. -// [0.351s][debug][gc,humongous] GC(3) Humongous region 2 (object size 4194320 @ 0x00000000fee00000) remset 0 code roots 0 marked 1 pinned count 0 reclaim candidate 1 type array 1 + // [0.351s][debug][gc,humongous] GC(3) Humongous region 2 (object size 4194320 @ 0x00000000fee00000) remset 0 code roots 0 marked 1 pinned count 0 reclaim candidate 1 type array 1 // Now check the result of the reclaim attempt. We are interested in the last such message (as mentioned above, we might get two). String patternString = "gc,humongous.* marked (\\d) pin.*candidate (\\d)"; @@ -152,6 +151,23 @@ public class TestEagerReclaimHumongousRegions { Asserts.assertTrue(expected.reclaimed == reclaimed, "Wrong log output reclaiming humongous region"); } + private static void runTest(ObjectType type, + ReferencePolicy refPolicy, + AllocationTiming timing, + String phase, + ExpectedState expected) throws Exception { + List vmArgs = testArgs(); + + ArrayList args = new ArrayList(vmArgs); + String log = runHelperVM(args, type, refPolicy, timing, phase); + verifyLog(log, timing, expected); + + ArrayList jfrArgs = new ArrayList(vmArgs); + jfrArgs.addLast("-XX:StartFlightRecording=settings=profile"); + String jfrLog = runHelperVM(jfrArgs, type, refPolicy, timing, phase); + verifyLog(jfrLog, timing, expected); + } + public static void main(String[] args) throws Exception { System.out.println("Tests checking eager reclaim for when the object is allocated before mark start."); runTest(ObjectType.TYPE_ARRAY, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.MARKED_CANDIDATE_RECLAIMED); diff --git a/test/hotspot/jtreg/gc/g1/humongousObjects/TestObjectCollected.java b/test/hotspot/jtreg/gc/g1/humongousObjects/TestObjectCollected.java index b1ddecd7290..b3a30d8f2f4 100644 --- a/test/hotspot/jtreg/gc/g1/humongousObjects/TestObjectCollected.java +++ b/test/hotspot/jtreg/gc/g1/humongousObjects/TestObjectCollected.java @@ -44,8 +44,8 @@ import java.lang.ref.WeakReference; * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * * @run main/othervm -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+WhiteBoxAPI -Xbootclasspath/a:. -Xms200m -Xmx200m -Xlog:gc - * -XX:InitiatingHeapOccupancyPercent=100 -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectCollected.gc.log + * -XX:+WhiteBoxAPI -Xbootclasspath/a:. -Xms200m -Xmx200m -Xlog:gc,gc+humongous=debug + * -XX:InitiatingHeapOccupancyPercent=100 -XX:G1HeapRegionSize=1M -Xlog:gc,gc+humongous=debug:file=TestObjectCollected.gc.log * gc.g1.humongousObjects.TestObjectCollected */ @@ -103,12 +103,22 @@ public class TestObjectCollected { public Reference create() { return new WeakReference<>(new byte[ALLOCATION_SIZE], referenceQueqe); } + + @Override + public boolean expectCleared() { + return true; + } }, SOFT { @Override public Reference create() { return new SoftReference<>(new byte[ALLOCATION_SIZE], referenceQueqe); } + + @Override + public boolean expectCleared() { + return false; + } }; private static final ReferenceQueue referenceQueqe = new ReferenceQueue<>(); @@ -120,6 +130,11 @@ public class TestObjectCollected { * @return weak/soft reference on byte array of ALLOCATION_SIZE */ public abstract Reference create(); + + /** + * For WeakReferences we expect the referent to always be cleared after GC. + */ + public abstract boolean expectCleared(); } @@ -162,8 +177,13 @@ public class TestObjectCollected { isRegionFree = WHITE_BOX.g1BelongsToFreeRegion(adr); boolean isObjCollected = isRegionFree || !isRegionHumongous; - - Asserts.assertEquals(isRefNulled, isObjCollected, + if (ref.expectCleared()) { + Asserts.assertEquals(isRefNulled, true, + "Reference.get() did not return null for a reference where it is expected"); + Asserts.assertEquals(isObjCollected, true, + "The humonogus object was not collected as expected"); + } else { + Asserts.assertEquals(isRefNulled, isObjCollected, String.format("There is an inconsistensy between reference and white box " + "method behavior - one considers object referenced with " + "%s type collected and another doesn't!\n" @@ -172,7 +192,7 @@ public class TestObjectCollected { reference.getClass().getSimpleName(), (isRefNulled ? "" : "not "), (isObjCollected ? "" : " not"))); - + } System.out.println("Passed"); } diff --git a/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestCycleEndMessage.java b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestCycleEndMessage.java new file mode 100644 index 00000000000..e7a4cfc9605 --- /dev/null +++ b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestCycleEndMessage.java @@ -0,0 +1,83 @@ +/* + * Copyright Amazon.com Inc. 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 Check that GC cycle end message contains generation name + * @library /test/lib / + * @requires vm.gc.Shenandoah + * + * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * TestCycleEndMessage + */ + +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.management.Notification; +import javax.management.NotificationEmitter; +import javax.management.NotificationListener; +import javax.management.openmbean.CompositeData; + +import com.sun.management.GarbageCollectionNotificationInfo; + +public class TestCycleEndMessage { + + public static void main(String[] args) throws Exception { + final AtomicBoolean foundGenerationInCycle = new AtomicBoolean(false); + + NotificationListener listener = new NotificationListener() { + @Override + public void handleNotification(Notification n, Object o) { + if (n.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) { + GarbageCollectionNotificationInfo info = GarbageCollectionNotificationInfo.from((CompositeData) n.getUserData()); + + String name = info.getGcName(); + String action = info.getGcAction(); + + System.out.println("Received: " + name + " / " + action); + + if (name.equals("Shenandoah Cycles") && + (action.contains("Global") || action.contains("Young") || action.contains("Old"))) { + foundGenerationInCycle.set(true); + } + } + } + }; + + for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) { + ((NotificationEmitter) bean).addNotificationListener(listener, null, null); + } + + System.gc(); + Thread.sleep(2000); + + if (!foundGenerationInCycle.get()) { + throw new IllegalStateException("Expected to find generation name (Global/Young/Old) in Shenandoah Cycles action message"); + } + + System.out.println("Test passed: Found generation name in cycle end message"); + } +} diff --git a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithAllocateHeapAt.java b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithAllocateHeapAt.java index 778d398d9d2..cf837c1c3dd 100644 --- a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithAllocateHeapAt.java +++ b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithAllocateHeapAt.java @@ -31,7 +31,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.G1 - * @requires vm.flavor == "server" & !vm.emulatedClient & os.family != "aix" + * @requires vm.flavor == "server" & os.family != "aix" * @summary Stress Java heap allocation with AllocateHeapAt flag using GC basher. * @run main/othervm/timeout=500 -Xlog:gc*=info -Xmx256m -server -XX:+UseG1GC -XX:AllocateHeapAt=. gc.stress.gcbasher.TestGCBasherWithAllocateHeapAt 120000 */ diff --git a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithG1.java b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithG1.java index 4f11ab361fb..a9095d73d9d 100644 --- a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithG1.java +++ b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithG1.java @@ -31,7 +31,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.G1 - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @summary Stress the G1 GC by trying to make old objects more likely to be garbage than young objects. * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx256m -XX:+UseG1GC gc.stress.gcbasher.TestGCBasherWithG1 120000 */ diff --git a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithParallel.java b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithParallel.java index ee5ab654462..1bb71531abd 100644 --- a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithParallel.java +++ b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithParallel.java @@ -31,7 +31,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Parallel - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @summary Stress the Parallel GC by trying to make old objects more likely to be garbage than young objects. * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx256m -XX:+UseParallelGC -XX:-UseGCOverheadLimit gc.stress.gcbasher.TestGCBasherWithParallel 120000 */ diff --git a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithSerial.java b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithSerial.java index 25650b6b9d5..ff6ad786302 100644 --- a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithSerial.java +++ b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithSerial.java @@ -31,7 +31,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Serial - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @summary Stress the Serial GC by trying to make old objects more likely to be garbage than young objects. * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx256m -XX:+UseSerialGC gc.stress.gcbasher.TestGCBasherWithSerial 120000 */ diff --git a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java index 0d1a4af9a2a..3bf0e59dce3 100644 --- a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java +++ b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java @@ -32,7 +32,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @summary Stress the Shenandoah GC by trying to make old objects more likely to be garbage than young objects. * * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions @@ -51,7 +51,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @summary Stress the Shenandoah GC by trying to make old objects more likely to be garbage than young objects. * * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions @@ -74,7 +74,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @summary Stress the Shenandoah GC by trying to make old objects more likely to be garbage than young objects. * * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions @@ -92,7 +92,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient & vm.opt.ClassUnloading != false + * @requires vm.flavor == "server" & vm.opt.ClassUnloading != false * @summary Stress Shenandoah GC with nmethod barrier forced deoptimization enabled. * * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions @@ -105,7 +105,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient & vm.opt.ClassUnloading != false + * @requires vm.flavor == "server" & vm.opt.ClassUnloading != false * @summary Stress Shenandoah GC with nmethod barrier forced deoptimization enabled. * * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions @@ -126,7 +126,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient & vm.opt.ClassUnloading != false + * @requires vm.flavor == "server" & vm.opt.ClassUnloading != false * @summary Stress Shenandoah GC with nmethod barrier forced deoptimization enabled. * * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions @@ -152,7 +152,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient & vm.opt.ClassUnloading != false + * @requires vm.flavor == "server" & vm.opt.ClassUnloading != false * @summary Stress Shenandoah GC with nmethod barrier forced deoptimization enabled. * * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions @@ -172,7 +172,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient & vm.opt.ClassUnloading != false + * @requires vm.flavor == "server" & vm.opt.ClassUnloading != false * @summary Stress Shenandoah GC with nmethod barrier forced deoptimization enabled. * * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions @@ -185,7 +185,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @summary Stress the Shenandoah GC by trying to make old objects more likely to be garbage than young objects. * * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions @@ -203,7 +203,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient & vm.opt.ClassUnloading != false + * @requires vm.flavor == "server" & vm.opt.ClassUnloading != false * @summary Stress Shenandoah GC with nmethod barrier forced deoptimization enabled. * * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions diff --git a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithZ.java b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithZ.java index d4a95fd9791..2007580f065 100644 --- a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithZ.java +++ b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithZ.java @@ -31,7 +31,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Z - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @summary Stress ZGC * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx384m -XX:+UseZGC gc.stress.gcbasher.TestGCBasherWithZ 120000 */ @@ -41,7 +41,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Z - * @requires vm.flavor == "server" & !vm.emulatedClient & vm.opt.ClassUnloading != false + * @requires vm.flavor == "server" & vm.opt.ClassUnloading != false * @summary Stress ZGC with nmethod barrier forced deoptimization enabled. * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx384m -XX:+UseZGC * -XX:+UnlockDiagnosticVMOptions -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline diff --git a/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTools.java b/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTools.java index 3dbedd61d12..e4185dd0f12 100644 --- a/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTools.java +++ b/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTools.java @@ -129,7 +129,8 @@ class TestStringDeduplicationTools { GarbageCollectionNotificationInfo info = GarbageCollectionNotificationInfo.from((CompositeData) n.getUserData()); // Shenandoah and Z GC also report GC pauses, skip them if (info.getGcName().startsWith("Shenandoah")) { - if ("end of GC cycle".equals(info.getGcAction())) { + String action = info.getGcAction(); + if (action != null && action.contains("GC cycle")) { gcCount++; } } else if (info.getGcName().startsWith("ZGC")) { diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/StackWalkNativeToJava.java b/test/hotspot/jtreg/runtime/ErrorHandling/StackWalkNativeToJava.java index a964a61003a..1e29afe5ff8 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/StackWalkNativeToJava.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/StackWalkNativeToJava.java @@ -33,7 +33,7 @@ import java.util.List; * @test StackWalkNativeToJava * @bug 8316309 * @summary Check that walking the stack works fine when going from C++ frame to Java frame. - * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" + * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64" * @requires os.family != "windows" * @requires vm.flagless * @library /test/lib diff --git a/test/hotspot/jtreg/runtime/ReservedStack/ReservedStackTestCompiler.java b/test/hotspot/jtreg/runtime/ReservedStack/ReservedStackTestCompiler.java index ac9e41b3236..2dc6b7e56a5 100644 --- a/test/hotspot/jtreg/runtime/ReservedStack/ReservedStackTestCompiler.java +++ b/test/hotspot/jtreg/runtime/ReservedStack/ReservedStackTestCompiler.java @@ -25,7 +25,7 @@ * @test ReservedStackTestCompiler * @summary Run ReservedStackTest with dedicated compilers C1 and C2. * - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @requires vm.opt.DeoptimizeALot != true * @library /test/lib * @modules java.base/jdk.internal.misc diff --git a/test/hotspot/jtreg/runtime/Unsafe/AllocateMemory.java b/test/hotspot/jtreg/runtime/Unsafe/AllocateMemory.java index 7d8d33b1225..b18bcb8cc13 100644 --- a/test/hotspot/jtreg/runtime/Unsafe/AllocateMemory.java +++ b/test/hotspot/jtreg/runtime/Unsafe/AllocateMemory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -61,7 +61,7 @@ public class AllocateMemory { // allocateMemory() should throw an OutOfMemoryError when the underlying malloc fails, // since we start with -XX:MallocLimit try { - address = unsafe.allocateMemory(100 * 1024 * 1024); + address = unsafe.allocateMemory(101 * 1024 * 1024); throw new RuntimeException("Did not get expected OutOfMemoryError"); } catch (OutOfMemoryError e) { // Expected diff --git a/test/hotspot/jtreg/runtime/Unsafe/Reallocate.java b/test/hotspot/jtreg/runtime/Unsafe/Reallocate.java index c15b931449d..dc0ce86fa1f 100644 --- a/test/hotspot/jtreg/runtime/Unsafe/Reallocate.java +++ b/test/hotspot/jtreg/runtime/Unsafe/Reallocate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -62,7 +62,7 @@ public class Reallocate { // Make sure we can throw an OOME when we fail to reallocate due to OOM try { - unsafe.reallocateMemory(address, 100 * 1024 * 1024); + unsafe.reallocateMemory(address, 101 * 1024 * 1024); } catch (OutOfMemoryError e) { // Expected return; diff --git a/test/hotspot/jtreg/runtime/cds/DeterministicClasslist.java b/test/hotspot/jtreg/runtime/cds/DeterministicClasslist.java new file mode 100644 index 00000000000..00f12a500a4 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/DeterministicClasslist.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2026, 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 8378371 + * @summary The same JDK build should always generate the same classlist file (no randomness). + * @requires vm.cds & vm.flagless + * @library /test/lib ./appcds/ + * @run driver DeterministicClasslist + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.compiler.InMemoryJavaCompiler; +import jdk.test.lib.helpers.ClassFileInstaller; + + +public class DeterministicClasslist { + + public static void compareClasslists(String base, String test) throws Exception { + File baseClasslistFile = new File(base); + File testClasslistFile = new File(test); + + BufferedReader baseReader = new BufferedReader(new FileReader(baseClasslistFile)); + BufferedReader testReader = new BufferedReader(new FileReader(testClasslistFile)); + + String baseLine, testLine; + while ((baseLine = baseReader.readLine()) != null) { + testLine = testReader.readLine(); + // Skip constant pool entries + if (baseLine.contains("@cp")) { + continue; + } + if (!baseLine.equals(testLine)) { + System.out.println(baseLine + " vs " + testLine); + throw new RuntimeException("Classlists differ"); + } + } + } + + static Path findFile(String path) { + Path root = Paths.get(System.getProperty("test.root", ".")); + // Move back to java root directory + root = root.getParent().getParent().getParent(); + + Path file = root.resolve(path); + if (Files.exists(file)) { + return file; + } + + return null; + } + + public static void main (String[] args) throws Exception { + String[] classlist = { "build/tools/classlist/HelloClasslist", "build/tools/classlist/HelloClasslist$1A", "build/tools/classlist/HelloClasslist$1B" }; + String appClass = classlist[0]; + String appJar; + String classDir = System.getProperty("test.classes"); + String baseClasslist = "base.classlist"; + String testClasslist = "test.classlist"; + + Path testPath = findFile("make/jdk/src/classes/build/tools/classlist/HelloClasslist.java"); + if (testPath == null) { + throw new RuntimeException("Could not find HelloClasslist"); + } + + String source = Files.readString(testPath); + Map compiledClasses = InMemoryJavaCompiler.compile(Map.of(appClass, source)); + + for (Entry e : compiledClasses.entrySet()) { + ClassFileInstaller.writeClassToDisk(e.getKey(), e.getValue(), classDir); + } + + JarBuilder.build("classlist", classlist); + appJar = TestCommon.getTestJar("classlist.jar"); + + CDSTestUtils.dumpClassList(baseClasslist, "-cp", appJar, "-Xint", appClass); + CDSTestUtils.dumpClassList(testClasslist, "-cp", appJar, "-Xint", appClass); + + compareClasslists(baseClasslist, testClasslist); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/MetaspaceAllocGaps.java b/test/hotspot/jtreg/runtime/cds/MetaspaceAllocGaps.java new file mode 100644 index 00000000000..65459263e36 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/MetaspaceAllocGaps.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2026, 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 + * @requires vm.cds + * @requires vm.flagless + * @requires vm.bits == 64 + * @bug 8376822 + * @summary Allocation gaps in the RW region caused by -XX:+UseCompactObjectHeaders should be reused + * @library /test/lib + * @build MetaspaceAllocGaps + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar hello.jar Hello + * @run driver MetaspaceAllocGaps + */ + +import jdk.test.lib.cds.SimpleCDSAppTester; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.OutputAnalyzer; + +public class MetaspaceAllocGaps { + public static void main(String[] args) throws Exception { + String appJar = ClassFileInstaller.getJarPath("hello.jar"); + for (int i = 0; i < 2; i++) { + String compressedOops = "-XX:" + (i == 0 ? "-" : "+") + "UseCompressedOops"; + SimpleCDSAppTester.of("MetaspaceAllocGaps" + i) + .addVmArgs("-Xlog:aot=debug,aot+alloc=trace", + "-XX:+UseCompactObjectHeaders") + .classpath(appJar) + .appCommandLine("Hello") + .setTrainingChecker((OutputAnalyzer out) -> { + // Typically all gaps should be filled. If not, we probably have a regression in C++ class ArchiveUtils. + // + // [0.422s][debug][aot ] Detailed metadata info (excluding heap region): + // [...] + // [0.422s][debug][aot ] Gap : 0 0 0.0 | 0 0 0.0 | 0 0 0.0 <<< look for this pattern + out.shouldMatch("Allocated [1-9][0-9]+ objects of [1-9][0-9]+ bytes in gaps .remain = 0 bytes") + .shouldMatch("debug.* Gap .*0[.]0.*0[.]0.*0[.]0") + .shouldNotMatch("Unexpected .* gaps .* for Klass alignment"); + }) + .setProductionChecker((OutputAnalyzer out) -> { + out.shouldContain("HelloWorld"); + }) + .runAOTWorkflow(); + } + } +} + +class Hello { + public static void main(String[] args) { + System.out.println("HelloWorld"); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java b/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java index 4e40ae3645e..8241b0f9a2e 100644 --- a/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java +++ b/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -61,8 +61,8 @@ public class TestCDSVMCrash { CDSTestUtils.executeAndLog(pb, "cds_vm_crash"); throw new Error("Expected VM to crash"); } catch(RuntimeException e) { - if (!e.getMessage().equals("Hotspot crashed")) { - throw new Error("Expected message: Hotspot crashed"); + if (!e.getMessage().contains("A fatal error has been detected")) { + throw new Error("Expected message: A fatal error has been detected"); } } System.out.println("PASSED"); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithAOTHeap.java b/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithAOTHeap.java index 567c8da73cb..719a4ee83f7 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithAOTHeap.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithAOTHeap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -65,7 +65,7 @@ public class TestZGCWithAOTHeap { String dumpGC = dumpWithZ ? Z : G1; String execGC = execWithZ ? Z : G1; String generalErrMsg = "Cannot use CDS heap data."; - String coopsErrMsg = generalErrMsg + " Selected GC not compatible -XX:-UseCompressedOops"; + String coopsErrMsg = "CDS heap data cannot be used by the selected GC. Please choose a different GC or rebuild AOT cache with -XX:+AOTStreamableObjects"; String coops = "-XX:-UseCompressedOops"; String coh = shouldUseCOH ? "-XX:+UseCompactObjectHeaders" : "-XX:-UseCompactObjectHeaders"; String stream = shouldStream ? "-XX:+AOTStreamableObjects" : "-XX:-AOTStreamableObjects"; diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ChangedJarFile.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ChangedJarFile.java index a717b267347..e86defdf816 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ChangedJarFile.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ChangedJarFile.java @@ -59,7 +59,7 @@ public class ChangedJarFile { tester.productionRun(new String[] {"-XX:AOTMode=auto", "-Xlog:aot"}, new String[] {"jarHasChanged"}); out.shouldMatch("This file is not the one used while building the " + - "AOT cache: '.*app.jar', timestamp has changed, size has changed"); + "AOT cache: '.*app.jar',.* size has changed"); } static class Tester extends CDSAppTester { diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/EarlyClassInit.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/EarlyClassInit.java new file mode 100644 index 00000000000..f78c3f83861 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/EarlyClassInit.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2026, 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 Early init of classes in the AOT cache + * @bug 8378731 + * @requires vm.cds.supports.aot.class.linking + * @library /test/lib + * @build EarlyClassInit + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar EarlyClassInitApp + * @run driver EarlyClassInit + */ + +import jdk.test.lib.cds.SimpleCDSAppTester; +import jdk.test.lib.process.OutputAnalyzer; + +public class EarlyClassInit { + public static void main(String... args) throws Exception { + SimpleCDSAppTester.of("EarlyClassInit") + .addVmArgs("-Xlog:aot+init=debug") + .classpath("app.jar") + .appCommandLine("EarlyClassInitApp") + .setProductionChecker((OutputAnalyzer out) -> { + out.shouldContain("java.lang.Object (aot-inited, early)") + .shouldContain("No early init java.lang.ClassLoader: needs runtimeSetup()") + .shouldContain("No early init java.security.SecureClassLoader: super type java.lang.ClassLoader not initialized") + .shouldContain("Calling java.lang.ClassLoader::runtimeSetup()") + .shouldContain("java.security.SecureClassLoader (aot-inited)"); + out.shouldContain("HelloWorld"); + }) + .runAOTWorkflow(); + } +} + +class EarlyClassInitApp { + public static void main(String[] args) { + System.out.println("HelloWorld"); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java index 9a9524eb2f1..728428afef2 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -32,6 +32,7 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar * TestApp * TestApp$Foo + * TestApp$Foo$A * TestApp$Foo$Bar * TestApp$Foo$ShouldBeExcluded * TestApp$Foo$ShouldBeExcludedChild @@ -43,6 +44,8 @@ */ import java.io.File; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Array; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; @@ -165,6 +168,8 @@ class TestApp { long start = System.currentTimeMillis(); while (System.currentTimeMillis() - start < 1000) { lambdaHotSpot(); + lambdaHotSpot2(); + invokeHandleHotSpot(); s.hotSpot2(); b.hotSpot3(); Taz.hotSpot4(); @@ -207,12 +212,52 @@ class TestApp { } } + interface A { + Object get(); + } + + // Lambdas that refer to excluded classes should not be AOT-resolved + static void lambdaHotSpot2() { + long start = System.currentTimeMillis(); + A a = ShouldBeExcluded::new; + while (System.currentTimeMillis() - start < 20) { + Object obj = (ShouldBeExcluded)a.get(); + } + } + + static void invokeHandleHotSpot() { + try { + invokeHandleHotSpotImpl(); + } catch (Throwable t) { + throw new RuntimeException("Unexpected", t); + } + } + + static void invokeHandleHotSpotImpl() throws Throwable { + MethodHandle constructorHandle = + MethodHandles.lookup().unreflectConstructor(ShouldBeExcluded.class.getConstructor()); + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < 20) { + // The JVM rewrites this to: + // invokehandle + // + // The AOT cache must not contain a java.lang.invoke.MethodType that refers to the + // ShouldBeExcluded class. + ShouldBeExcluded o = (ShouldBeExcluded)constructorHandle.invoke(); + if (o.getClass() != ShouldBeExcluded.class) { + throw new RuntimeException("Unexpected object: " + o); + } + } + } + static void doit(Runnable r) { r.run(); } // All subclasses of jdk.jfr.Event are excluded from the CDS archive. static class ShouldBeExcluded extends jdk.jfr.Event { + public ShouldBeExcluded() {} + int f = (int)(System.currentTimeMillis()) + 123; int m() { return f + 456; @@ -275,4 +320,3 @@ class TestApp { } } } - diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addmods/AddmodsOption.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addmods/AddmodsOption.java index 10e17f2072f..cf9ffb2403a 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addmods/AddmodsOption.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addmods/AddmodsOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -47,7 +47,6 @@ public class AddmodsOption { final String allModulePath = "ALL-MODULE-PATH"; final String loggingOption = "-Xlog:aot=debug,aot+module=debug,aot+heap=info,cds=debug,module=trace"; final String versionPattern = "java.[0-9][0-9].*"; - final String subgraphCannotBeUsed = "subgraph jdk.internal.module.ArchivedBootLayer cannot be used because full module graph is disabled"; final String warningIncubator = "WARNING: Using incubator modules: jdk.incubator.vector"; String archiveName = TestCommon.getNewArchiveName("addmods-option"); TestCommon.setCurrentArchiveName(archiveName); @@ -81,16 +80,14 @@ public class AddmodsOption { "-version"); oa.shouldHaveExitValue(0) .shouldContain("Mismatched values for property jdk.module.addmods") - .shouldContain("runtime jdk.incubator.vector dump time jdk.jconsole") - .shouldContain(subgraphCannotBeUsed); + .shouldContain("runtime jdk.incubator.vector dump time jdk.jconsole"); // no module specified during runtime oa = TestCommon.execCommon( loggingOption, "-version"); oa.shouldHaveExitValue(0) - .shouldContain("jdk.httpserver specified during dump time but not during runtime") - .shouldContain(subgraphCannotBeUsed); + .shouldContain("jdk.httpserver specified during dump time but not during runtime"); // dump an archive without the --add-modules option archiveName = TestCommon.getNewArchiveName("no-addmods-option"); @@ -111,8 +108,7 @@ public class AddmodsOption { oa.shouldHaveExitValue(0) .shouldContain("jdk.jconsole specified during runtime but not during dump time") // version of the jdk.httpserver module, e.g. java 22-ea - .shouldMatch(versionPattern) - .shouldContain(subgraphCannotBeUsed); + .shouldMatch(versionPattern); // dump an archive with an incubator module, -add-modules jdk.incubator.vector archiveName = TestCommon.getNewArchiveName("incubator-module"); @@ -137,7 +133,6 @@ public class AddmodsOption { // module is not restored from archive .shouldContain("define_module(): creation of module: jdk.incubator.vector") .shouldContain("WARNING: Using incubator modules: jdk.incubator.vector") - .shouldContain("subgraph jdk.internal.module.ArchivedBootLayer is not recorde") .shouldHaveExitValue(0); if (Compiler.isJVMCIEnabled()) { diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addopens/AddopensOption.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addopens/AddopensOption.java index 636169ef3cb..6f1864f90bd 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addopens/AddopensOption.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addopens/AddopensOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -43,7 +43,6 @@ public class AddopensOption { final String addOpensTimeFormat = "java.base/java.time.format=ALL-UNNAMED"; final String loggingOption = "-Xlog:aot=debug,aot+module=debug,aot+heap=info,cds=debug,module=trace"; final String versionPattern = "java.[0-9][0-9].*"; - final String subgraphCannotBeUsed = "subgraph jdk.internal.module.ArchivedBootLayer cannot be used because full module graph is disabled"; final String warningIncubator = "WARNING: Using incubator modules: jdk.incubator.vector"; String archiveName = TestCommon.getNewArchiveName("addopens-option"); TestCommon.setCurrentArchiveName(archiveName); @@ -76,16 +75,14 @@ public class AddopensOption { "-version"); oa.shouldHaveExitValue(0) .shouldContain("Mismatched values for property jdk.module.addopens") - .shouldContain("runtime java.base/java.nio=ALL-UNNAMED dump time java.base/java.time.format=ALL-UNNAMED") - .shouldContain(subgraphCannotBeUsed); + .shouldContain("runtime java.base/java.nio=ALL-UNNAMED dump time java.base/java.time.format=ALL-UNNAMED"); // no module specified during runtime oa = TestCommon.execCommon( loggingOption, "-version"); oa.shouldHaveExitValue(0) - .shouldContain("jdk.httpserver specified during dump time but not during runtime") - .shouldContain(subgraphCannotBeUsed); + .shouldContain("jdk.httpserver specified during dump time but not during runtime"); // dump an archive without the --add-opens option archiveName = TestCommon.getNewArchiveName("no-addopens-option"); @@ -106,8 +103,7 @@ public class AddopensOption { oa.shouldHaveExitValue(0) .shouldContain("java.base/java.time.format=ALL-UNNAMED specified during runtime but not during dump time") // version of the jdk.httpserver module, e.g. java 22-ea - .shouldMatch(versionPattern) - .shouldContain(subgraphCannotBeUsed); + .shouldMatch(versionPattern); // dump an archive with -add-opens java.base/java.nio=ALL-UNNAMED archiveName = TestCommon.getNewArchiveName("addopens-java-nio"); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/ModuleOption.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/ModuleOption.java index 3001b8dd2ca..6339538bbb2 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/ModuleOption.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/ModuleOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -42,7 +42,6 @@ public class ModuleOption { // e.g. JDK 22: "java 22" // JDK 22.0.1: "java 22.0.1" final String versionPattern = "java.[0-9][0-9].*"; - final String subgraphCannotBeUsed = "subgraph jdk.internal.module.ArchivedBootLayer cannot be used because full module graph is disabled"; String archiveName = TestCommon.getNewArchiveName("module-option"); TestCommon.setCurrentArchiveName(archiveName); @@ -70,16 +69,14 @@ public class ModuleOption { "-m", "jdk.compiler/com.sun.tools.javac.Main", "-version"); oa.shouldHaveExitValue(0) - .shouldContain("Mismatched values for property jdk.module.main: runtime jdk.compiler dump time jdk.httpserver") - .shouldContain(subgraphCannotBeUsed); + .shouldContain("Mismatched values for property jdk.module.main: runtime jdk.compiler dump time jdk.httpserver"); // no module specified during runtime oa = TestCommon.execCommon( loggingOption, "-version"); oa.shouldHaveExitValue(0) - .shouldContain("Mismatched values for property jdk.module.main: jdk.httpserver specified during dump time but not during runtime") - .shouldContain(subgraphCannotBeUsed); + .shouldContain("Mismatched values for property jdk.module.main: jdk.httpserver specified during dump time but not during runtime"); // dump an archive without the module option archiveName = TestCommon.getNewArchiveName("no-module-option"); @@ -98,8 +95,7 @@ public class ModuleOption { oa.shouldHaveExitValue(0) .shouldContain("Mismatched values for property jdk.module.main: jdk.httpserver specified during runtime but not during dump time") // version of the jdk.httpserver module, e.g. java 22-ea - .shouldMatch(versionPattern) - .shouldContain(subgraphCannotBeUsed); + .shouldMatch(versionPattern); // dump an archive with an incubator module, -m jdk.incubator.vector archiveName = TestCommon.getNewArchiveName("incubator-module"); @@ -122,7 +118,6 @@ public class ModuleOption { // module is not restored from archive .shouldContain("define_module(): creation of module: jdk.incubator.vector") .shouldContain("WARNING: Using incubator modules: jdk.incubator.vector") - .shouldContain("subgraph jdk.internal.module.ArchivedBootLayer is not recorde") .shouldContain("module jdk.incubator.vector does not have a ModuleMainClass attribute, use -m /") .shouldHaveExitValue(1); } diff --git a/test/hotspot/jtreg/runtime/cds/serviceability/ReplaceCriticalClasses.java b/test/hotspot/jtreg/runtime/cds/serviceability/ReplaceCriticalClasses.java index 408ab0582b1..99b2714a08f 100644 --- a/test/hotspot/jtreg/runtime/cds/serviceability/ReplaceCriticalClasses.java +++ b/test/hotspot/jtreg/runtime/cds/serviceability/ReplaceCriticalClasses.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -190,7 +190,6 @@ public class ReplaceCriticalClasses { if (checkSubgraph) { if (expectShared) { if (!out.getOutput().contains("Unable to map at required address in java heap")) { - out.shouldContain(subgraphInit); // If the subgraph is successfully initialized, the specified shared class must not be rewritten. out.shouldNotContain("Rewriting done."); } diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/AbstractMethodError/AbstractMethodErrorTest.java b/test/hotspot/jtreg/runtime/exceptionMsgs/AbstractMethodError/AbstractMethodErrorTest.java index a7063ecd2dc..508dcf197e3 100644 --- a/test/hotspot/jtreg/runtime/exceptionMsgs/AbstractMethodError/AbstractMethodErrorTest.java +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/AbstractMethodError/AbstractMethodErrorTest.java @@ -25,7 +25,7 @@ /** * @test * @summary Check that the verbose message of the AME is printed correctly. - * @requires !(os.arch=="arm") & vm.flavor == "server" & !vm.emulatedClient & vm.compMode=="Xmixed" & !vm.graal.enabled & vm.opt.UseJVMCICompiler != true & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel==4) + * @requires !(os.arch=="arm") & vm.flavor == "server" & vm.compMode=="Xmixed" & !vm.graal.enabled & vm.opt.UseJVMCICompiler != true & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel==4) * @requires vm.opt.DeoptimizeALot != true * @library /test/lib / * @build jdk.test.whitebox.WhiteBox diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/IncompatibleClassChangeError/IncompatibleClassChangeErrorTest.java b/test/hotspot/jtreg/runtime/exceptionMsgs/IncompatibleClassChangeError/IncompatibleClassChangeErrorTest.java index 77738191f19..5891ff60c42 100644 --- a/test/hotspot/jtreg/runtime/exceptionMsgs/IncompatibleClassChangeError/IncompatibleClassChangeErrorTest.java +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/IncompatibleClassChangeError/IncompatibleClassChangeErrorTest.java @@ -26,7 +26,7 @@ * @test * @summary Check that the verbose message of ICCE is printed correctly. * The test forces errors in vtable stubs and interpreter. - * @requires !(os.arch=="arm") & vm.flavor == "server" & !vm.emulatedClient & vm.compMode=="Xmixed" & (!vm.graal.enabled | vm.opt.TieredCompilation == true) & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel==4) + * @requires !(os.arch=="arm") & vm.flavor == "server" & vm.compMode=="Xmixed" & (!vm.graal.enabled | vm.opt.TieredCompilation == true) & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel==4) * @library /test/lib / * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox diff --git a/test/hotspot/jtreg/runtime/logging/GenerateOopMapTest.java b/test/hotspot/jtreg/runtime/logging/GenerateOopMapTest.java new file mode 100644 index 00000000000..6e114ff6b1d --- /dev/null +++ b/test/hotspot/jtreg/runtime/logging/GenerateOopMapTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2026, 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 GenerateOopMap + * @bug 8379015 + * @requires vm.flagless + * @modules java.base/jdk.internal.misc + * @library /test/lib + * @run driver GenerateOopMapTest + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.Platform; + +public class GenerateOopMapTest { + + static String infoPattern = "[generateoopmap]"; + static String debugPattern = "[generateoopmap] Basicblock#0 begins at:"; + static String tracePattern = "[trace][generateoopmap] 5 vars = 'r' stack = 'v' monitors = '' \tifne"; + static String traceDetailPattern = "[generateoopmap] 0 vars = ( r |slot0) invokestatic()V"; + + static void test() throws Exception { + // Don't print much with info level. + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:generateoopmap=info", + "GenerateOopMapTest", "test"); + OutputAnalyzer o = new OutputAnalyzer(pb.start()); + o.shouldNotContain(infoPattern).shouldHaveExitValue(0); + + // Prints bytecodes and BasicBlock information in debug. + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:generateoopmap=debug", + "GenerateOopMapTest", "test"); + o = new OutputAnalyzer(pb.start()); + o.shouldContain(debugPattern).shouldHaveExitValue(0); + + // Prints ref/val for each bytecode in trace. + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:generateoopmap=trace", + "GenerateOopMapTest", "test"); + o = new OutputAnalyzer(pb.start()); + o.shouldContain(tracePattern).shouldHaveExitValue(0); + + // Prints extra stuff with detailed. Not sure how useful this is but keep it for now. + if (Platform.isDebugBuild()) { + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:generateoopmap=trace", + "-XX:+Verbose", + "GenerateOopMapTest", "test"); + o = new OutputAnalyzer(pb.start()); + o.shouldContain(traceDetailPattern).shouldHaveExitValue(0); + } + }; + + public static void main(String... args) throws Exception { + System.gc(); + if (args.length == 0) { + test(); + } + } +} diff --git a/test/hotspot/jtreg/runtime/os/AvailableProcessors.java b/test/hotspot/jtreg/runtime/os/AvailableProcessors.java index 18201d99127..51d6742b832 100644 --- a/test/hotspot/jtreg/runtime/os/AvailableProcessors.java +++ b/test/hotspot/jtreg/runtime/os/AvailableProcessors.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -38,6 +38,9 @@ import jtreg.SkippedException; import java.util.ArrayList; import java.io.File; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; public class AvailableProcessors { @@ -73,13 +76,17 @@ public class AvailableProcessors { "AvailableProcessors"); int[] expected = new int[] { 1, available/2, available-1, available }; + int cpuId = getFirstAllowedCpu(); + if (cpuId == -1) { + throw new SkippedException("Could not determine allowed CPU cores"); + } for (int i : expected) { System.out.println("Testing for " + i + " processors ..."); - int max = i - 1; + int max = i - 1 + cpuId; ArrayList cmdline = new ArrayList<>(master.command()); // prepend taskset command - cmdline.add(0, "0-" + max); + cmdline.add(0, cpuId + "-" + max); cmdline.add(0, "-c"); cmdline.add(0, taskset); // append expected processor count @@ -104,4 +111,40 @@ public class AvailableProcessors { else System.out.println(SUCCESS_STRING + available); } + + /** + * Retrieves the first available CPU core ID allowed for the current process on Linux. + * + * @return The first CPU ID in Cpus_allowed_list, or -1 if unavailable. + */ + static int getFirstAllowedCpu() { + final String statusFile = "/proc/self/status"; + final String targetKey = "Cpus_allowed_list:"; + + try (BufferedReader br = new BufferedReader(new FileReader(statusFile))) { + String line; + while ((line = br.readLine()) != null) { + // Look for the line starting with "Cpus_allowed_list:" + if (line.startsWith(targetKey)) { + // Extract the value part, e.g., "0-15,32-47" or "80,82,84" + String listValue = line.substring(targetKey.length()).trim(); + if (listValue.isEmpty()) return -1; + + // Get the first segment before any comma (e.g., "0-15" from "0-15,32") + String firstSegment = listValue.split(",")[0]; + + // If it is a range (e.g., "80-159"), take the start number + if (firstSegment.contains("-")) { + return Integer.parseInt(firstSegment.split("-")[0]); + } else { + // If it is a single ID (e.g., "1"), parse it directly + return Integer.parseInt(firstSegment); + } + } + } + } catch (IOException | NumberFormatException | ArrayIndexOutOfBoundsException e) { + throw new RuntimeException("Failed to read or parse " + statusFile, e); + } + return -1; + } } diff --git a/test/hotspot/jtreg/runtime/verifier/UninitThisAcmp.jasm b/test/hotspot/jtreg/runtime/verifier/UninitThisAcmp.jasm new file mode 100644 index 00000000000..c1729a78653 --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/UninitThisAcmp.jasm @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025, 2026, 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. + */ + +class UninitThisAcmp version 69:0 +{ + public Method "":"()V" + stack 2 locals 2 + { + new class java/lang/Object; + dup; + invokespecial Method java/lang/Object."":"()V"; + astore_1; + aload_0; + aload_1; + if_acmpne L14; + nop; + L14: stack_frame_type append; + locals_map class java/lang/Object; + aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; + } +} // end Class UninitThisAcmp diff --git a/test/hotspot/jtreg/runtime/verifier/UninitThisAcmpOld.jasm b/test/hotspot/jtreg/runtime/verifier/UninitThisAcmpOld.jasm new file mode 100644 index 00000000000..3bcc1e09b67 --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/UninitThisAcmpOld.jasm @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025, 2026, 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. + */ + +class UninitThisAcmpOld version 49:0 +{ + public Method "":"()V" + stack 2 locals 2 + { + new class java/lang/Object; + dup; + invokespecial Method java/lang/Object."":"()V"; + astore_1; + aload_0; + aload_1; + if_acmpne L14; + nop; + L14: aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; + } +} // end Class UninitThisAcmpOld diff --git a/test/hotspot/jtreg/runtime/verifier/UninitThisIfNull.jasm b/test/hotspot/jtreg/runtime/verifier/UninitThisIfNull.jasm new file mode 100644 index 00000000000..57a82a9f1e4 --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/UninitThisIfNull.jasm @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025, 2026, 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. + */ + +class UninitThisIfNull version 69:0 +{ + public Method "":"()V" + stack 2 locals 2 + { + new class java/lang/Object; + dup; + invokespecial Method java/lang/Object."":"()V"; + astore_1; + aload_0; + ifnonnull L14; + nop; + L14: stack_frame_type append; + locals_map class java/lang/Object; + aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; + } +} // end Class UninitThisIfNull diff --git a/test/hotspot/jtreg/runtime/verifier/UninitThisIfNullOld.jasm b/test/hotspot/jtreg/runtime/verifier/UninitThisIfNullOld.jasm new file mode 100644 index 00000000000..fbe96c2eef0 --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/UninitThisIfNullOld.jasm @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025, 2026, 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. + */ + +class UninitThisIfNullOld version 49:0 +{ + public Method "":"()V" + stack 2 locals 2 + { + new class java/lang/Object; + dup; + invokespecial Method java/lang/Object."":"()V"; + astore_1; + aload_0; + ifnonnull L14; + nop; + L14: aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; + } +} // end Class UninitThisIfNullOld diff --git a/test/hotspot/jtreg/runtime/verifier/UninitializedAcmp.jasm b/test/hotspot/jtreg/runtime/verifier/UninitializedAcmp.jasm new file mode 100644 index 00000000000..b8ce17cb16b --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/UninitializedAcmp.jasm @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2025, 2026, 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. + */ + +class UninitializedAcmp version 69:0 +{ + Method "":"()V" + stack 5 locals 1 + { + aload_0; + invokespecial Method java/lang/Object."":"()V"; + aload_0; + L1: new class java/lang/Object; + dup; + dup; + dup; + if_acmpne L18; + nop; + L18: stack_frame_type full; + locals_map class UninitializedAcmp; + stack_map class UninitializedAcmp, at L1, at L1; + invokespecial Method java/lang/Object."":"()V"; + return; + } +} // end Class UninitializedAcmp diff --git a/test/hotspot/jtreg/runtime/verifier/UninitializedAcmpOld.jasm b/test/hotspot/jtreg/runtime/verifier/UninitializedAcmpOld.jasm new file mode 100644 index 00000000000..d16cc5c6779 --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/UninitializedAcmpOld.jasm @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025, 2026, 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. + */ + +class UninitializedAcmpOld version 49:0 +{ + Method "":"()V" + stack 5 locals 1 + { + aload_0; + invokespecial Method java/lang/Object."":"()V"; + aload_0; + L1: new class java/lang/Object; + dup; + dup; + dup; + if_acmpne L18; + nop; + L18: invokespecial Method java/lang/Object."":"()V"; + return; + } +} // end Class UninitializedAcmpOld diff --git a/test/hotspot/jtreg/runtime/verifier/UninitializedIfNull.jasm b/test/hotspot/jtreg/runtime/verifier/UninitializedIfNull.jasm new file mode 100644 index 00000000000..4e7251f073d --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/UninitializedIfNull.jasm @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025, 2026, 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. + */ + +class UninitializedIfNull version 69:0 +{ + Method "":"()V" + stack 3 locals 1 + { + aload_0; + invokespecial Method java/lang/Object."":"()V"; + L1: new class java/lang/Object; + dup; + dup; + ifnonnull L18; + nop; + L18: stack_frame_type full; + locals_map class UninitializedIfNull; + stack_map at L1, at L1; + invokespecial Method java/lang/Object."":"()V"; + return; + } +} // end Class UninitializedIfNull diff --git a/test/hotspot/jtreg/runtime/verifier/UninitializedIfNullOld.jasm b/test/hotspot/jtreg/runtime/verifier/UninitializedIfNullOld.jasm new file mode 100644 index 00000000000..5bffba887a8 --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/UninitializedIfNullOld.jasm @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2025, 2026, 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. + */ + +class UninitializedIfNullOld version 49:0 +{ + Method "":"()V" + stack 3 locals 1 + { + aload_0; + invokespecial Method java/lang/Object."":"()V"; + L1: new class java/lang/Object; + dup; + dup; + ifnonnull L18; + nop; + L18: invokespecial Method java/lang/Object."":"()V"; + return; + } +} // end Class UninitializedIfNullOld diff --git a/test/hotspot/jtreg/runtime/verifier/UninitializedThisVerificationTest.java b/test/hotspot/jtreg/runtime/verifier/UninitializedThisVerificationTest.java new file mode 100644 index 00000000000..075e9f2058e --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/UninitializedThisVerificationTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2025, 2026, 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 8366743 + * @summary Test spec rules for uninitialized + * @compile UninitThisAcmp.jasm UninitThisIfNull.jasm + * UninitializedIfNull.jasm UninitializedAcmp.jasm + * UninitThisAcmpOld.jasm UninitThisIfNullOld.jasm + * UninitializedAcmpOld.jasm UninitializedIfNullOld.jasm + * @run main/othervm -Xlog:verification UninitializedThisVerificationTest + */ + +public class UninitializedThisVerificationTest { + + public static void main(String[] args) throws Exception { + String[] testNames = { "UninitThisAcmp", "UninitThisIfNull", + "UninitializedAcmp", "UninitializedIfNull", + "UninitThisAcmpOld", "UninitThisIfNullOld", + "UninitializedAcmpOld", "UninitializedIfNullOld" }; + int fails = 0; + for (String test : testNames) { + System.out.println("Testing " + test); + try { + Class c = Class.forName(test); + System.out.println("Failed"); + fails++; + } catch (java.lang.VerifyError e) { + System.out.println("Passed"); + } + } + + if (fails > 0) { + throw new RuntimeException("Failed: Expected VerifyError in " + fails + " tests"); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerDirectivesDCMDTest.java b/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerDirectivesDCMDTest.java index 5e4f73cedd5..690726317b0 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerDirectivesDCMDTest.java +++ b/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerDirectivesDCMDTest.java @@ -49,7 +49,7 @@ public class CompilerDirectivesDCMDTest { public void run(CommandExecutor executor) { - if (Platform.isServer() && !Platform.isEmulatedClient()) { + if (Platform.isServer()) { filename = System.getProperty("test.src", ".") + File.separator + "control2.txt"; } else { filename = System.getProperty("test.src", ".") + File.separator + "control1.txt"; diff --git a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java index 5b4a1c7e663..65a89b152b6 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java @@ -28,7 +28,6 @@ * @requires vm.jvmti & * vm.gc != "Epsilon" & * vm.flavor == "server" & - * !vm.emulatedClient & * (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) & * vm.compMode == "Xmixed" * @library /test/lib /test/hotspot/jtreg diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendResume3/SuspendResume3.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendResume3/SuspendResume3.java index fbc9ff2d5b3..ae49ce29ed3 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendResume3/SuspendResume3.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendResume3/SuspendResume3.java @@ -35,6 +35,11 @@ import java.time.Instant; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Phaser; +import java.lang.management.LockInfo; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; + import jvmti.JVMTIUtils; public class SuspendResume3 { @@ -88,7 +93,12 @@ public class SuspendResume3 { w1Sync1.arriveAndAwaitAdvance(); // Let worker2 block on monitor w2Sync1.arriveAndAwaitAdvance(); - await(worker2, Thread.State.BLOCKED); + // Wait until worker2 blocks trying to acquire lock. We can't just check + // for a BLOCKED state because method arriveAndAwaitAdvance might involve + // extra class loading/initialization where worker2 could be seen as BLOCKED + // and thus be suspended below. If the main thread then tries to access those + // same classes before resuming worker2 the test would deadlock. + awaitBlockedOnLock(worker2); // Suspend worker2 JVMTIUtils.suspendThread(worker2); @@ -144,6 +154,21 @@ public class SuspendResume3 { } } + /** + * Waits for the given thread to block trying to acquire lock's monitor. + */ + private void awaitBlockedOnLock(Thread thread) throws InterruptedException { + while (true) { + ThreadInfo threadInfo = ManagementFactory.getThreadMXBean().getThreadInfo(thread.threadId()); + assertTrue(threadInfo != null, "getThreadInfo() failed"); + LockInfo lockInfo = threadInfo.getLockInfo(); + if (lockInfo != null && lockInfo.getIdentityHashCode() == System.identityHashCode(lock)) { + break; + } + Thread.sleep(10); + } + } + private static void assertTrue(boolean condition, String msg) { if (!condition) { throw new RuntimeException(msg); diff --git a/test/hotspot/jtreg/serviceability/threads/ThreadInfoTest.java b/test/hotspot/jtreg/serviceability/threads/ThreadInfoTest.java new file mode 100644 index 00000000000..b5db455cc20 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/threads/ThreadInfoTest.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2026, 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 jdk.test.lib.Utils; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; + +import java.util.ArrayList; +import java.util.List; + +/* + * @test + * @bug 8323792 + * @summary Make sure that jmm_GetThreadInfo() call does not crash JVM + * @library /test/lib + * @modules java.management + * @run main/othervm ThreadInfoTest + * + * @comment Exercise getThreadInfo(ids, 0). Depth parameter of zero means + * no VM operation, which could crash with Threads starting and ending. + */ + +public class ThreadInfoTest { + private static com.sun.management.ThreadMXBean mbean = + (com.sun.management.ThreadMXBean)ManagementFactory.getThreadMXBean(); + + private static final int NUM_THREADS = 2; + static long[] ids = new long[NUM_THREADS]; + static ThreadInfo[] infos = new ThreadInfo[NUM_THREADS]; + static volatile int count = 0; + static int ITERATIONS = 4; + + public static void main(String[] argv) throws Exception { + boolean replacing = false; + + startThreads(ids, NUM_THREADS); + new MyGetThreadInfoThread(ids).start(); + new MyReplacerThread(ids).start(); + for (int i = 0; i < ITERATIONS; i++) { + do { + count = countInfo(infos); + System.out.println("Iteration " + i + ": ThreadInfos found (Threads alive): " + count); + goSleep(100); + } while (count > 0); + } + } + + private static Thread newThread(int i) { + Thread thread = new MyThread(i); + thread.setDaemon(true); + return thread; + } + + private static void startThreads(long[] ids, int count) { + System.out.println("Starting " + count + " Threads..."); + Thread[] threads = new Thread[count]; + for (int i = 0; i < count; i++) { + threads[i] = newThread(i); + threads[i].start(); + ids[i] = threads[i].getId(); + } + System.out.println(ids); + } + + // Count ThreadInfo from array, return how many are non-null. + private static int countInfo(ThreadInfo[] info) { + int count = 0; + if (info != null) { + int i = 0; + for (ThreadInfo ti: info) { + if (ti != null) { + count++; + } + i++; + } + } + return count; + } + + private static int replaceThreads(long[] ids, ThreadInfo[] info) { + int replaced = 0; + if (info != null) { + for (int i = 0; i < info.length; i++) { + ThreadInfo ti = info[i]; + if (ti == null) { + Thread thread = newThread(i); + thread.start(); + ids[i] = thread.getId(); + replaced++; + } + } + } + return replaced; + } + + private static void goSleep(long ms) { + try { + Thread.sleep(ms); + } catch (InterruptedException e) { + System.out.println("Unexpected exception is thrown: " + e); + } + } + + // A Thread which replaces Threads in the shared array of threads. + static class MyReplacerThread extends Thread { + long[] ids; + + public MyReplacerThread(long[] ids) { + this.ids = ids; + this.setDaemon(true); + } + + public void run() { + boolean replacing = false; + while (true) { + if (replacing) { + replaceThreads(ids, infos); + } + if (count < 10) { + replacing = true; + } + if (count > 20) { + replacing = false; + } + goSleep(1); + } + } + } + + // A Thread which lives for a short while. + static class MyThread extends Thread { + long endTimeMs; + + public MyThread(long n) { + super("MyThread-" + n); + endTimeMs = (n * n * 10) + System.currentTimeMillis(); + } + + public void run() { + try { + long sleep = Math.max(1, endTimeMs - System.currentTimeMillis()); + goSleep(sleep); + } catch (Exception e) { + System.out.println(Thread.currentThread().getName() + ": " + e); + } + } + } + + // A Thread to continually call getThreadInfo on a shared array of thread ids. + static class MyGetThreadInfoThread extends Thread { + long[] ids; + + public MyGetThreadInfoThread(long[] ids) { + this.ids = ids; + this.setDaemon(true); + } + + public void run() { + while (true) { + infos = mbean.getThreadInfo(ids, 0); + goSleep(10); + } + } + } +} diff --git a/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/Utils.java b/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/Utils.java index ec24d4f186d..20234677929 100644 --- a/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/Utils.java +++ b/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/Utils.java @@ -86,12 +86,10 @@ public class Utils { INITIAL_COMP_LEVEL = 1; } else { String vmName = System.getProperty("java.vm.name"); - String vmInfo = System.getProperty("java.vm.info"); - boolean isEmulatedClient = (vmInfo != null) && vmInfo.contains("emulated-client"); - if (Utils.endsWithIgnoreCase(vmName, " Server VM") && !isEmulatedClient) { + if (Utils.endsWithIgnoreCase(vmName, " Server VM")) { INITIAL_COMP_LEVEL = 4; } else if (Utils.endsWithIgnoreCase(vmName, " Client VM") - || Utils.endsWithIgnoreCase(vmName, " Minimal VM") || isEmulatedClient) { + || Utils.endsWithIgnoreCase(vmName, " Minimal VM")) { INITIAL_COMP_LEVEL = 1; } else { throw new RuntimeException("Unknown VM: " + vmName); diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java index b6881fede75..fc8a92cf9a2 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java @@ -329,7 +329,7 @@ public class TestIRMatching { System.out.flush(); String output = baos.toString(); findIrIds(output, "testMatchAllIf50", 1, 22); - findIrIds(output, "testMatchNoneIf50", -1, -1); + assertNoIds(output, "testMatchNoneIf50"); runWithArguments(FlagComparisons.class, "-XX:TLABRefillWasteFraction=49"); System.out.flush(); @@ -431,12 +431,14 @@ public class TestIRMatching { private static void findIrIds(String output, String method, int... numbers) { StringBuilder builder = new StringBuilder(); - builder.append(method); + builder.append(method).append(": "); for (int i = 0; i < numbers.length; i+=2) { int start = numbers[i]; int endIncluded = numbers[i + 1]; for (int j = start; j <= endIncluded; j++) { - builder.append(","); + if (j != numbers[0]) { + builder.append(", "); + } builder.append(j); } } @@ -445,6 +447,13 @@ public class TestIRMatching { System.lineSeparator())); } } + + private static void assertNoIds(String output, String methodName) { + String applicableIRRules = output.split("Applicable IR Rules")[1]; + if (applicableIRRules.contains(methodName)) { + addException(new RuntimeException("Should not find ids for \"" + methodName + "\"" + System.lineSeparator())); + } + } } class AndOr1 { diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java index 6193b6aad0c..facaefaa8f3 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java @@ -48,6 +48,7 @@ import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.$; import static compiler.lib.template_framework.Template.addDataName; import static compiler.lib.template_framework.DataName.Mutability.MUTABLE; +import static compiler.lib.template_framework.DataName.Mutability.MUTABLE_OR_IMMUTABLE; import compiler.lib.template_framework.library.Hooks; import compiler.lib.template_framework.library.CodeGenerationDataNameType; @@ -129,7 +130,8 @@ public class TestPrimitiveTypes { } // Finally, test the type by creating some DataNames (variables), and sampling - // from them. There should be no cross-over between the types. + // from them. Sampling exactly should not lead to any conversion and sampling + // subtypes should only lead to widening conversions. // IMPORTANT: since we are adding the DataName via an inserted Template, we // must chose a "transparentScope", so that the DataName escapes. If we // instead chose "scope", the test would fail, because it later @@ -150,6 +152,14 @@ public class TestPrimitiveTypes { """ )); + var assignmentTemplate = Template.make("lhsType", (PrimitiveType lhsType) -> scope( + dataNames(MUTABLE).exactOf(lhsType).sampleAndLetAs("lhs"), + dataNames(MUTABLE_OR_IMMUTABLE).subtypeOf(lhsType).sampleAndLetAs("rhs"), + """ + #lhs = #rhs; + """ + )); + var namesTemplate = Template.make(() -> scope( """ public static void test_names() { @@ -161,10 +171,16 @@ public class TestPrimitiveTypes { ).toList() ), """ - // Now sample: + // Sample exactly: """, Collections.nCopies(10, CodeGenerationDataNameType.PRIMITIVE_TYPES.stream().map(sampleTemplate::asToken).toList() + ), + """ + // Sample subtypes: + """, + Collections.nCopies(10, + CodeGenerationDataNameType.PRIMITIVE_TYPES.stream().map(assignmentTemplate::asToken).toList() ) )), """ diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java index b34538c39c1..609b0936079 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java @@ -41,6 +41,7 @@ import compiler.lib.template_framework.TemplateToken; import static compiler.lib.template_framework.Template.scope; import compiler.lib.template_framework.library.CodeGenerationDataNameType; import compiler.lib.template_framework.library.Expression; +import compiler.lib.template_framework.library.Expression.Nesting; /** * This tests the use of the {@link Expression} from the template library. This is @@ -174,44 +175,74 @@ public class TestExpression { Expression e4 = Expression.make(myTypeA1, "<", myTypeA, ">"); Expression e5 = Expression.make(myTypeA, "[", myTypeB, "]"); - Expression e1e2 = e1.nestRandomly(List.of(e2)); - Expression e1ex = e1.nestRandomly(List.of(e3, e2, e3)); - Expression e1e4 = e1.nestRandomly(List.of(e3, e4, e3)); - Expression e1ey = e1.nestRandomly(List.of(e3, e3)); + Expression e1e2 = e1.nestRandomly(List.of(e2), Nesting.SUBTYPE); + Expression e1e2Exact = e1.nestRandomly(List.of(e2), Nesting.EXACT); + Expression e1ex = e1.nestRandomly(List.of(e3, e2, e3), Nesting.SUBTYPE); + Expression e1exExact = e1.nestRandomly(List.of(e3, e2, e3), Nesting.EXACT); + Expression e1e4 = e1.nestRandomly(List.of(e3, e4, e3), Nesting.SUBTYPE); + Expression e1e4Exact = e1.nestRandomly(List.of(e3, e4, e3), Nesting.EXACT); + Expression e1ey = e1.nestRandomly(List.of(e3, e3), Nesting.SUBTYPE); + Expression e1eyExact = e1.nestRandomly(List.of(e3, e3), Nesting.EXACT); // 5-deep nesting of e1 - Expression deep1 = Expression.nestRandomly(myTypeA, List.of(e1, e3), 5); + Expression deep1 = Expression.nestRandomly(myTypeA, List.of(e1, e3), 5, Nesting.SUBTYPE); + Expression deep1Exact = Expression.nestRandomly(myTypeA, List.of(e1, e3), 5, Nesting.EXACT); // Alternating pattern - Expression deep2 = Expression.nestRandomly(myTypeA, List.of(e5, e3), 5); + Expression deep2 = Expression.nestRandomly(myTypeA, List.of(e5, e3), 5, Nesting.SUBTYPE); + Expression deep2Exact = Expression.nestRandomly(myTypeA, List.of(e5, e3), 5, Nesting.SUBTYPE); var template = Template.make(() -> scope( "xx", e1e2.toString(), "yy\n", + "xx", e1e2Exact.toString(), "yy\n", "xx", e1ex.toString(), "yy\n", + "xx", e1exExact.toString(), "yy\n", "xx", e1e4.toString(), "yy\n", + "xx", e1e4Exact.toString(), "yy\n", "xx", e1ey.toString(), "yy\n", + "xx", e1eyExact.toString(), "yy\n", "xx", deep1.toString(), "yy\n", + "xx", deep1Exact.toString(), "yy\n", "xx", deep2.toString(), "yy\n", + "xx", deep2Exact.toString(), "yy\n", "xx", e1e2.asToken(List.of("a")), "yy\n", + "xx", e1e2Exact.asToken(List.of("a")), "yy\n", "xx", e1ex.asToken(List.of("a")), "yy\n", + "xx", e1exExact.asToken(List.of("a")), "yy\n", "xx", e1e4.asToken(List.of("a")), "yy\n", + "xx", e1e4Exact.asToken(List.of("a")), "yy\n", "xx", e1ey.asToken(List.of("a")), "yy\n", + "xx", e1eyExact.asToken(List.of("a")), "yy\n", "xx", deep1.asToken(List.of("a")), "yy\n", - "xx", deep2.asToken(List.of("a")), "yy\n" + "xx", deep1Exact.asToken(List.of("a")), "yy\n", + "xx", deep2.asToken(List.of("a")), "yy\n", + "xx", deep2Exact.asToken(List.of("a")), "yy\n" )); String expected = """ xxExpression["[(", MyTypeA, ")]"]yy xxExpression["[(", MyTypeA, ")]"]yy + xxExpression["[(", MyTypeA, ")]"]yy + xxExpression["[(", MyTypeA, ")]"]yy xxExpression["[<", MyTypeA, ">]"]yy xxExpression["[", MyTypeA, "]"]yy + xxExpression["[", MyTypeA, "]"]yy + xxExpression["[", MyTypeA, "]"]yy + xxExpression["[[[[[", MyTypeA, "]]]]]"]yy xxExpression["[[[[[", MyTypeA, "]]]]]"]yy xxExpression["[{[{[", MyTypeB, "]}]}]"]yy + xxExpression["[{[{[", MyTypeB, "]}]}]"]yy + xx[(a)]yy + xx[(a)]yy xx[(a)]yy xx[(a)]yy xx[]yy xx[a]yy + xx[a]yy + xx[a]yy xx[[[[[a]]]]]yy + xx[[[[[a]]]]]yy + xx[{[{[a]}]}]yy xx[{[{[a]}]}]yy """; String code = template.render(); diff --git a/test/jaxp/javax/xml/jaxp/functional/catalog/CatalogReferCircularityTest.java b/test/jaxp/javax/xml/jaxp/functional/catalog/CatalogReferCircularityTest.java index c88827c1a39..612011f8eb6 100644 --- a/test/jaxp/javax/xml/jaxp/functional/catalog/CatalogReferCircularityTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/catalog/CatalogReferCircularityTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -23,7 +23,6 @@ package catalog; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -31,6 +30,7 @@ import javax.xml.catalog.CatalogException; import javax.xml.catalog.CatalogResolver; import static catalog.CatalogTestUtils.catalogResolver; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test @@ -55,7 +55,7 @@ public class CatalogReferCircularityTest { "catalogReferCircle-left.xml" }) public void testReferCircularity(String catalogFile) { CatalogResolver resolver = catalogResolver(catalogFile); - Assertions.assertThrows( + assertThrows( CatalogException.class, () -> resolver.resolveEntity(null, "http://remote/dtd/ghost/docGhost.dtd")); } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/DurationTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/DurationTest.java index cb8f5c56048..83839033b62 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/DurationTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/DurationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -23,52 +23,50 @@ package javax.xml.datatype.ptests; -import static javax.xml.datatype.DatatypeConstants.DAYS; -import static javax.xml.datatype.DatatypeConstants.HOURS; -import static javax.xml.datatype.DatatypeConstants.MINUTES; -import static javax.xml.datatype.DatatypeConstants.MONTHS; -import static javax.xml.datatype.DatatypeConstants.SECONDS; -import static javax.xml.datatype.DatatypeConstants.YEARS; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Calendar; -import java.util.function.Function; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.Duration; import javax.xml.namespace.QName; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Calendar; +import java.util.function.Function; -import org.testng.Assert; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import static javax.xml.datatype.DatatypeConstants.DAYS; +import static javax.xml.datatype.DatatypeConstants.HOURS; +import static javax.xml.datatype.DatatypeConstants.MINUTES; +import static javax.xml.datatype.DatatypeConstants.MONTHS; +import static javax.xml.datatype.DatatypeConstants.SECONDS; +import static javax.xml.datatype.DatatypeConstants.YEARS; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.datatype.ptests.DurationTest + * @run junit/othervm javax.xml.datatype.ptests.DurationTest * @summary Class containing the test cases for Duration. */ public class DurationTest { - private DatatypeFactory datatypeFactory; + private static DatatypeFactory datatypeFactory = null; - /* - * Setup. - */ - @BeforeClass - public void setup() throws DatatypeConfigurationException { + @BeforeAll + public static void setup() throws DatatypeConfigurationException { datatypeFactory = DatatypeFactory.newInstance(); } - @DataProvider(name = "legal-number-duration") - public Object[][] getLegalNumberDuration() { + public static Object[][] getLegalNumberDuration() { return new Object[][] { // is positive, year, month, day, hour, minute, second { true, 1, 1, 1, 1, 1, 1 }, @@ -82,13 +80,13 @@ public class DurationTest { * Test for constructor Duration(boolean isPositive,int years,int months, * int days,int hours,int minutes,int seconds). */ - @Test(dataProvider = "legal-number-duration") + @ParameterizedTest + @MethodSource("getLegalNumberDuration") public void checkNumberDurationPos(boolean isPositive, int years, int months, int days, int hours, int minutes, int seconds) { datatypeFactory.newDuration(isPositive, years, months, days, hours, minutes, seconds); } - @DataProvider(name = "illegal-number-duration") - public Object[][] getIllegalNumberDuration() { + public static Object[][] getIllegalNumberDuration() { return new Object[][] { // is positive, year, month, day, hour, minute, second { true, 1, 1, -1, 1, 1, 1 }, @@ -103,13 +101,15 @@ public class DurationTest { * int days,int hours,int minutes,int seconds), if any of the fields is * negative should throw IllegalArgumentException. */ - @Test(expectedExceptions = IllegalArgumentException.class, dataProvider = "illegal-number-duration") + @ParameterizedTest + @MethodSource("getIllegalNumberDuration") public void checkDurationNumberNeg(boolean isPositive, int years, int months, int days, int hours, int minutes, int seconds) { - datatypeFactory.newDuration(isPositive, years, months, days, hours, minutes, seconds); + assertThrows( + IllegalArgumentException.class, + () -> datatypeFactory.newDuration(isPositive, years, months, days, hours, minutes, seconds)); } - @DataProvider(name = "legal-bigint-duration") - public Object[][] getLegalBigIntegerDuration() { + public static Object[][] getLegalBigIntegerDuration() { return new Object[][] { // is positive, year, month, day, hour, minute, second { true, zero, zero, zero, zero, zero, new BigDecimal(zero) }, @@ -125,14 +125,14 @@ public class DurationTest { * years,BigInteger months, BigInteger days,BigInteger hours,BigInteger * minutes,BigDecimal seconds). */ - @Test(dataProvider = "legal-bigint-duration") + @ParameterizedTest + @MethodSource("getLegalBigIntegerDuration") public void checkBigIntegerDurationPos(boolean isPositive, BigInteger years, BigInteger months, BigInteger days, BigInteger hours, BigInteger minutes, BigDecimal seconds) { datatypeFactory.newDuration(isPositive, years, months, days, hours, minutes, seconds); } - @DataProvider(name = "illegal-bigint-duration") - public Object[][] getIllegalBigIntegerDuration() { + public static Object[][] getIllegalBigIntegerDuration() { return new Object[][] { // is positive, year, month, day, hour, minute, second { true, null, null, null, null, null, null }, @@ -146,79 +146,66 @@ public class DurationTest { * minutes,BigDecimal seconds), if all the fields are null should throw * IllegalArgumentException. */ - @Test(expectedExceptions = IllegalArgumentException.class, dataProvider = "illegal-bigint-duration") - public void checkBigIntegerDurationNeg(boolean isPositive, BigInteger years, BigInteger months, BigInteger days, BigInteger hours, BigInteger minutes, - BigDecimal seconds) { - datatypeFactory.newDuration(isPositive, years, months, days, hours, minutes, seconds); - } - - @DataProvider(name = "legal-millisec-duration") - public Object[][] getLegalMilliSecondDuration() { - return new Object[][] { { 1000000 }, { 0 }, { Long.MAX_VALUE }, { Long.MIN_VALUE } - - }; + @ParameterizedTest + @MethodSource("getIllegalBigIntegerDuration") + public void checkBigIntegerDurationNeg( + boolean isPositive, BigInteger years, BigInteger months, BigInteger days, BigInteger hours, BigInteger minutes, BigDecimal seconds) { + assertThrows(IllegalArgumentException.class, () -> datatypeFactory.newDuration(isPositive, years, months, days, hours, minutes, seconds)); } /* * Test for constructor Duration(long durationInMilliSeconds) */ - @Test(dataProvider = "legal-millisec-duration") + @ParameterizedTest + @ValueSource(longs={ 1000000, 0, Long.MAX_VALUE, Long.MIN_VALUE }) public void checkMilliSecondDuration(long millisec) { datatypeFactory.newDuration(millisec); } - @DataProvider(name = "legal-lexical-duration") - public Object[][] getLegalLexicalDuration() { - return new Object[][] { { "P1Y1M1DT1H1M1S" }, { "-P1Y1M1DT1H1M1S" } }; - } - /* * Test for constructor Duration(java.lang.String lexicalRepresentation) */ - @Test(dataProvider = "legal-lexical-duration") + @ParameterizedTest + @ValueSource(strings={ "P1Y1M1DT1H1M1S", "-P1Y1M1DT1H1M1S" }) public void checkLexicalDurationPos(String lexRepresentation) { datatypeFactory.newDuration(lexRepresentation); } - @DataProvider(name = "illegal-lexical-duration") - public Object[][] getIllegalLexicalDuration() { - return new Object[][] { - { null }, - { "P1Y1M1DT1H1M1S " }, - { " P1Y1M1DT1H1M1S" }, - { "X1Y1M1DT1H1M1S" }, - { "" }, - { "P1Y2MT" } // The designator 'T' shall be absent if all of the time items are absent in "PnYnMnDTnHnMnS" - }; + /* + * Test for constructor Duration(java.lang.String lexicalRepresentation), + * null should throw NullPointerException + */ + @Test + public void checkLexicalDurationNull() { + assertThrows(NullPointerException.class, () -> datatypeFactory.newDuration(null)); } /* * Test for constructor Duration(java.lang.String lexicalRepresentation), - * null should throw NullPointerException, invalid lex should throw - * IllegalArgumentException + * invalid lex should throw IllegalArgumentException */ - @Test(expectedExceptions = { NullPointerException.class, IllegalArgumentException.class }, dataProvider = "illegal-lexical-duration") + @ParameterizedTest + @ValueSource(strings={ "P1Y1M1DT1H1M1S ", " P1Y1M1DT1H1M1S", "X1Y1M1DT1H1M1S", "", "P1Y2MT" }) public void checkLexicalDurationNeg(String lexRepresentation) { - datatypeFactory.newDuration(lexRepresentation); + assertThrows(IllegalArgumentException.class, () -> datatypeFactory.newDuration(lexRepresentation)); } - @DataProvider(name = "equal-duration") - public Object[][] getEqualDurations() { + public static Object[][] getEqualDurations() { return new Object[][] { { "P1Y1M1DT1H1M1S", "P1Y1M1DT1H1M1S" } }; } /* * Test for compare() both durations valid and equal. */ - @Test(dataProvider = "equal-duration") + @ParameterizedTest + @MethodSource("getEqualDurations") public void checkDurationEqual(String lexRepresentation1, String lexRepresentation2) { Duration duration1 = datatypeFactory.newDuration(lexRepresentation1); Duration duration2 = datatypeFactory.newDuration(lexRepresentation2); - assertTrue(duration1.equals(duration2)); + assertEquals(duration1, duration2); } - @DataProvider(name = "greater-duration") - public Object[][] getGreaterDuration() { + public static Object[][] getGreaterDuration() { return new Object[][] { { "P1Y1M1DT1H1M2S", "P1Y1M1DT1H1M1S" }, { "P1Y1M1DT1H1M1S", "-P1Y1M1DT1H1M2S" }, @@ -229,15 +216,15 @@ public class DurationTest { /* * Test for compare() both durations valid and lhs > rhs. */ - @Test(dataProvider = "greater-duration") + @ParameterizedTest + @MethodSource("getGreaterDuration") public void checkDurationCompare(String lexRepresentation1, String lexRepresentation2) { Duration duration1 = datatypeFactory.newDuration(lexRepresentation1); Duration duration2 = datatypeFactory.newDuration(lexRepresentation2); - assertTrue(duration1.compare(duration2) == DatatypeConstants.GREATER); + assertEquals(DatatypeConstants.GREATER, duration1.compare(duration2)); } - @DataProvider(name = "not-equal-duration") - public Object[][] getNotEqualDurations() { + public static Object[][] getNotEqualDurations() { return new Object[][] { { "P1Y1M1DT1H1M1S", "-P1Y1M1DT1H1M1S" }, { "P2Y1M1DT1H1M1S", "P1Y1M1DT1H1M1S" } }; @@ -246,15 +233,15 @@ public class DurationTest { /* * Test for equals() both durations valid and lhs not equals rhs. */ - @Test(dataProvider = "not-equal-duration") + @ParameterizedTest + @MethodSource("getNotEqualDurations") public void checkDurationNotEqual(String lexRepresentation1, String lexRepresentation2) { Duration duration1 = datatypeFactory.newDuration(lexRepresentation1); Duration duration2 = datatypeFactory.newDuration(lexRepresentation2); - Assert.assertNotEquals(duration1, duration2); + assertNotEquals(duration1, duration2); } - @DataProvider(name = "duration-sign") - public Object[][] getDurationAndSign() { + public static Object[][] getDurationAndSign() { return new Object[][] { { "P0Y0M0DT0H0M0S", 0 }, { "P1Y0M0DT0H0M0S", 1 }, @@ -264,10 +251,11 @@ public class DurationTest { /* * Test for Duration.getSign(). */ - @Test(dataProvider = "duration-sign") - public void checkDurationSign(String lexRepresentation, int sign) { + @ParameterizedTest + @MethodSource("getDurationAndSign") + public void checkDurationSign(String lexRepresentation, int expectedSign) { Duration duration = datatypeFactory.newDuration(lexRepresentation); - assertEquals(duration.getSign(), sign); + assertEquals(expectedSign, duration.getSign()); } /* @@ -278,10 +266,9 @@ public class DurationTest { Duration durationPos = datatypeFactory.newDuration("P1Y0M0DT0H0M0S"); Duration durationNeg = datatypeFactory.newDuration("-P1Y0M0DT0H0M0S"); - assertEquals(durationPos.negate(), durationNeg); - assertEquals(durationNeg.negate(), durationPos); - assertEquals(durationPos.negate().negate(), durationPos); - + assertEquals(durationNeg, durationPos.negate()); + assertEquals(durationPos, durationNeg.negate()); + assertEquals(durationPos, durationPos.negate().negate()); } /* @@ -342,24 +329,22 @@ public class DurationTest { /* * Test Duration.isSet(Field) throws NPE if the field parameter is null. */ - @Test(expectedExceptions = NullPointerException.class) public void checkDurationIsSetNeg() { Duration duration = datatypeFactory.newDuration(true, 0, 0, 0, 0, 0, 0); - duration.isSet(null); + assertThrows(NullPointerException.class, () -> duration.isSet(null)); } /* * Test for -getField(DatatypeConstants.Field) DatatypeConstants.Field is * null - throws NPE. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void checkDurationGetFieldNeg() { - Duration duration67 = datatypeFactory.newDuration("P1Y1M1DT1H1M1S"); - duration67.getField(null); + Duration duration = datatypeFactory.newDuration("P1Y1M1DT1H1M1S"); + assertThrows(NullPointerException.class, () -> duration.getField(null)); } - @DataProvider(name = "duration-fields") - public Object[][] getDurationAndFields() { + public static Object[][] getDurationAndFields() { return new Object[][] { { "P1Y1M1DT1H1M1S", one, one, one, one, one, new BigDecimal(one) }, { "PT1M", null, null, null, null, one, null }, @@ -369,21 +354,21 @@ public class DurationTest { /* * Test for Duration.getField(DatatypeConstants.Field). */ - @Test(dataProvider = "duration-fields") + @ParameterizedTest + @MethodSource("getDurationAndFields") public void checkDurationGetField(String lexRepresentation, BigInteger years, BigInteger months, BigInteger days, BigInteger hours, BigInteger minutes, BigDecimal seconds) { Duration duration = datatypeFactory.newDuration(lexRepresentation); - assertEquals(duration.getField(YEARS), years); - assertEquals(duration.getField(MONTHS), months); - assertEquals(duration.getField(DAYS), days); - assertEquals(duration.getField(HOURS), hours); - assertEquals(duration.getField(MINUTES), minutes); - assertEquals(duration.getField(SECONDS), seconds); + assertEquals(years, duration.getField(YEARS)); + assertEquals(months, duration.getField(MONTHS)); + assertEquals(days, duration.getField(DAYS)); + assertEquals(hours, duration.getField(HOURS)); + assertEquals(minutes, duration.getField(MINUTES)); + assertEquals(seconds, duration.getField(SECONDS)); } - @DataProvider(name = "number-string") - public Object[][] getNumberAndString() { + public static Object[][] getNumberAndString() { return new Object[][] { // is positive, year, month, day, hour, minute, second, lexical { true, 1, 1, 1, 1, 1, 1, "P1Y1M1DT1H1M1S" }, @@ -396,22 +381,22 @@ public class DurationTest { /* * Test for - toString(). */ - @Test(dataProvider = "number-string") + @ParameterizedTest + @MethodSource("getNumberAndString") public void checkDurationToString(boolean isPositive, int years, int months, int days, int hours, int minutes, int seconds, String lexical) { - Duration duration = datatypeFactory.newDuration(isPositive, years, months, days, hours, minutes, seconds); - assertEquals(duration.toString(), lexical); + Duration duration = datatypeFactory.newDuration(isPositive, years, months, days, hours, minutes, seconds); + assertEquals(lexical, duration.toString()); - assertEquals(datatypeFactory.newDuration(duration.toString()), duration); + assertEquals(duration, datatypeFactory.newDuration(duration.toString())); } - @DataProvider(name = "duration-field") - public Object[][] getDurationAndField() { - Function getyears = duration -> duration.getYears(); - Function getmonths = duration -> duration.getMonths(); - Function getdays = duration -> duration.getDays(); - Function gethours = duration -> duration.getHours(); - Function getminutes = duration -> duration.getMinutes(); - Function getseconds = duration -> duration.getSeconds(); + public static Object[][] getDurationAndField() { + Function getyears = Duration::getYears; + Function getmonths = Duration::getMonths; + Function getdays = Duration::getDays; + Function gethours = Duration::getHours; + Function getminutes = Duration::getMinutes; + Function getseconds = Duration::getSeconds; return new Object[][] { { "P1Y1M1DT1H1M1S", getyears, 1 }, { "P1M1DT1H1M1S", getyears, 0 }, @@ -431,10 +416,11 @@ public class DurationTest { /* * Test for Duration.getYears(), getMonths(), etc. */ - @Test(dataProvider = "duration-field") - public void checkDurationGetOneField(String lexRepresentation, Function getter, int value) { + @ParameterizedTest + @MethodSource("getDurationAndField") + public void checkDurationGetOneField(String lexRepresentation, Function getter, int expectedValue) { Duration duration = datatypeFactory.newDuration(lexRepresentation); - assertEquals(getter.apply(duration).intValue(), value); + assertEquals(expectedValue, getter.apply(duration).intValue()); } /* @@ -443,7 +429,7 @@ public class DurationTest { @Test public void checkDurationGetSecondsField() { Duration duration85 = datatypeFactory.newDuration("P1Y1M1DT1H1M100000000S"); - assertEquals((duration85.getField(SECONDS)).intValue(), 100000000); + assertEquals(100000000, (duration85.getField(SECONDS)).intValue()); } /* @@ -452,9 +438,9 @@ public class DurationTest { */ @Test public void checkDurationGetTimeInMillis() { - Duration duration86 = datatypeFactory.newDuration("PT1M1S"); - Calendar calendar86 = Calendar.getInstance(); - assertEquals(duration86.getTimeInMillis(calendar86), 61000); + Duration duration = datatypeFactory.newDuration("PT1M1S"); + Calendar calendar = Calendar.getInstance(); + assertEquals(61000, duration.getTimeInMillis(calendar)); } /* @@ -462,15 +448,13 @@ public class DurationTest { * between startInstant and startInstant plus this Duration throws NPE if * startInstant parameter is null. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void checkDurationGetTimeInMillisNeg() { - Duration duration87 = datatypeFactory.newDuration("PT1M1S"); - Calendar calendar87 = null; - duration87.getTimeInMillis(calendar87); + Duration duration = datatypeFactory.newDuration("PT1M1S"); + assertThrows(NullPointerException.class, () -> duration.getTimeInMillis((Calendar) null)); } - @DataProvider(name = "duration-for-hash") - public Object[][] getDurationsForHash() { + public static Object[][] getDurationsForHash() { return new Object[][] { { "P1Y1M1DT1H1M1S", "P1Y1M1DT1H1M1S" }, { "P1D", "PT24H" }, @@ -483,17 +467,17 @@ public class DurationTest { * Test for Duration.hashcode(). hashcode() should return same value for * some equal durations. */ - @Test(dataProvider = "duration-for-hash") + @ParameterizedTest + @MethodSource("getDurationsForHash") public void checkDurationHashCode(String lexRepresentation1, String lexRepresentation2) { Duration duration1 = datatypeFactory.newDuration(lexRepresentation1); Duration duration2 = datatypeFactory.newDuration(lexRepresentation2); int hash1 = duration1.hashCode(); int hash2 = duration2.hashCode(); - assertTrue(hash1 == hash2, " generated hash1 : " + hash1 + " generated hash2 : " + hash2); + assertEquals(hash1, hash2, " generated hash1 : " + hash1 + " generated hash2 : " + hash2); } - @DataProvider(name = "duration-for-add") - public Object[][] getDurationsForAdd() { + public static Object[][] getDurationsForAdd() { return new Object[][] { // initVal, addVal, resultVal { "P1Y1M1DT1H1M1S", "P1Y1M1DT1H1M1S", "P2Y2M2DT2H2M2S" }, @@ -504,7 +488,8 @@ public class DurationTest { /* * Test for add(Duration rhs). */ - @Test(dataProvider = "duration-for-add") + @ParameterizedTest + @MethodSource("getDurationsForAdd") public void checkDurationAdd(String initVal, String addVal, String result) { Duration durationInit = datatypeFactory.newDuration(initVal); Duration durationAdd = datatypeFactory.newDuration(addVal); @@ -513,25 +498,32 @@ public class DurationTest { assertEquals(durationInit.add(durationAdd), durationResult); } - @DataProvider(name = "duration-for-addneg") - public Object[][] getDurationsForAddNeg() { + public static Object[][] getDurationsForAddNeg() { return new Object[][] { // initVal, addVal - { "P1Y1M1DT1H1M1S", null }, { "P1Y", "-P1D" }, { "-P1Y", "P1D" }, }; } /* - * Test for add(Duration rhs) 'rhs' is null , should throw NPE. "1 year" + - * "-1 day" or "-1 year" + "1 day" should throw IllegalStateException + * Test for add(Duration rhs). + * "1 year" + "-1 day" or "-1 year" + "1 day" should throw IllegalStateException */ - @Test(expectedExceptions = { NullPointerException.class, IllegalStateException.class }, dataProvider = "duration-for-addneg") + @ParameterizedTest + @MethodSource("getDurationsForAddNeg") public void checkDurationAddNeg(String initVal, String addVal) { Duration durationInit = datatypeFactory.newDuration(initVal); Duration durationAdd = addVal == null ? null : datatypeFactory.newDuration(addVal); + assertThrows(IllegalStateException.class, () -> durationInit.add(durationAdd)); + } - durationInit.add(durationAdd); + /* + * Test for add(Duration rhs) 'rhs' is null , should throw NPE. + */ + @Test + public void checkDurationAddNull() { + Duration durationInit = datatypeFactory.newDuration("P1Y1M1DT1H1M1S"); + assertThrows(NullPointerException.class, () -> durationInit.add(null)); } /* @@ -540,15 +532,14 @@ public class DurationTest { * Bug # 4972785 UnsupportedOperationException is expected * */ - @Test(expectedExceptions = UnsupportedOperationException.class) + @Test public void checkDurationCompareLarge() { String duration1Lex = "P100000000000000000000D"; String duration2Lex = "PT2400000000000000000000H"; Duration duration1 = datatypeFactory.newDuration(duration1Lex); Duration duration2 = datatypeFactory.newDuration(duration2Lex); - duration1.compare(duration2); - + assertThrows(UnsupportedOperationException.class, () -> duration1.compare(duration2)); } /* @@ -562,25 +553,24 @@ public class DurationTest { // DURATION Duration duration = datatypeFactory.newDuration("P1Y1M1DT1H1M1S"); QName duration_xmlSchemaType = duration.getXMLSchemaType(); - assertEquals(duration_xmlSchemaType, DatatypeConstants.DURATION, "Expected DatatypeConstants.DURATION, returned " + duration_xmlSchemaType.toString()); + assertEquals(DatatypeConstants.DURATION, duration_xmlSchemaType, "Expected DatatypeConstants.DURATION, returned " + duration_xmlSchemaType.toString()); // DURATION_DAYTIME Duration duration_dayTime = datatypeFactory.newDuration("P1DT1H1M1S"); QName duration_dayTime_xmlSchemaType = duration_dayTime.getXMLSchemaType(); - assertEquals(duration_dayTime_xmlSchemaType, DatatypeConstants.DURATION_DAYTIME, "Expected DatatypeConstants.DURATION_DAYTIME, returned " + assertEquals(DatatypeConstants.DURATION_DAYTIME, duration_dayTime_xmlSchemaType, "Expected DatatypeConstants.DURATION_DAYTIME, returned " + duration_dayTime_xmlSchemaType.toString()); // DURATION_YEARMONTH Duration duration_yearMonth = datatypeFactory.newDuration("P1Y1M"); QName duration_yearMonth_xmlSchemaType = duration_yearMonth.getXMLSchemaType(); - assertEquals(duration_yearMonth_xmlSchemaType, DatatypeConstants.DURATION_YEARMONTH, "Expected DatatypeConstants.DURATION_YEARMONTH, returned " + assertEquals(DatatypeConstants.DURATION_YEARMONTH, duration_yearMonth_xmlSchemaType, "Expected DatatypeConstants.DURATION_YEARMONTH, returned " + duration_yearMonth_xmlSchemaType.toString()); } - private final int undef = DatatypeConstants.FIELD_UNDEFINED; - private final BigInteger zero = BigInteger.ZERO; - private final BigInteger one = BigInteger.ONE; - + private final static int undef = DatatypeConstants.FIELD_UNDEFINED; + private final static BigInteger zero = BigInteger.ZERO; + private final static BigInteger one = BigInteger.ONE; } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/FactoryNewInstanceTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/FactoryNewInstanceTest.java index 7b75562bd87..642dfad6810 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/FactoryNewInstanceTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/FactoryNewInstanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -23,25 +23,27 @@ package javax.xml.datatype.ptests; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNotSame; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertEquals; +import jaxp.library.JAXPDataProvider; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.Duration; -import jaxp.library.JAXPDataProvider; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @bug 8169778 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.datatype.ptests.FactoryNewInstanceTest + * @build jaxp.library.JAXPDataProvider + * @run junit/othervm javax.xml.datatype.ptests.FactoryNewInstanceTest * @summary Tests for DatatypeFactory.newInstance(factoryClassName , classLoader) */ public class FactoryNewInstanceTest { @@ -50,9 +52,11 @@ public class FactoryNewInstanceTest { "com.sun.org.apache.xerces.internal.jaxp.datatype.DatatypeFactoryImpl"; private static final String DATATYPE_FACTORY_CLASSNAME = DEFAULT_IMPL_CLASS; - @DataProvider(name = "parameters") - public Object[][] getValidateParameters() { - return new Object[][] { { DATATYPE_FACTORY_CLASSNAME, null }, { DATATYPE_FACTORY_CLASSNAME, this.getClass().getClassLoader() } }; + public static Object[][] getValidateParameters() { + return new Object[][] { + { DATATYPE_FACTORY_CLASSNAME, null }, + { DATATYPE_FACTORY_CLASSNAME, FactoryNewInstanceTest.class.getClassLoader() }, + }; } /** @@ -66,8 +70,8 @@ public class FactoryNewInstanceTest { DatatypeFactory dtf2 = DatatypeFactory.newInstance(); assertNotSame(dtf1, dtf2, "same instance returned:"); assertSame(dtf1.getClass(), dtf2.getClass(), - "unexpected class mismatch for newDefaultInstance():"); - assertEquals(dtf1.getClass().getName(), DEFAULT_IMPL_CLASS); + "unexpected class mismatch for newDefaultInstance():"); + assertEquals(DEFAULT_IMPL_CLASS, dtf1.getClass().getName()); } /* @@ -76,7 +80,8 @@ public class FactoryNewInstanceTest { * implementation of javax.xml.datatype.DatatypeFactory , should return * newInstance of DatatypeFactory */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("getValidateParameters") public void testNewInstance(String factoryClassName, ClassLoader classLoader) throws DatatypeConfigurationException { DatatypeFactory dtf = DatatypeFactory.newInstance(DATATYPE_FACTORY_CLASSNAME, null); Duration duration = dtf.newDuration(true, 1, 1, 1, 1, 1, 1); @@ -89,9 +94,11 @@ public class FactoryNewInstanceTest { * java.lang.ClassLoader classLoader) factoryClassName is null , should * throw DatatypeConfigurationException */ - @Test(expectedExceptions = DatatypeConfigurationException.class, dataProvider = "new-instance-neg", dataProviderClass = JAXPDataProvider.class) + @ParameterizedTest + @MethodSource("jaxp.library.JAXPDataProvider#newInstanceNeg") public void testNewInstanceNeg(String factoryClassName, ClassLoader classLoader) throws DatatypeConfigurationException { - DatatypeFactory.newInstance(factoryClassName, classLoader); + assertThrows( + DatatypeConfigurationException.class, + () -> DatatypeFactory.newInstance(factoryClassName, classLoader)); } - } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/XMLGregorianCalendarTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/XMLGregorianCalendarTest.java index d9df1a2cddd..8365f2f3ad5 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/XMLGregorianCalendarTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/XMLGregorianCalendarTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -23,56 +23,54 @@ package javax.xml.datatype.ptests; -import static java.util.Calendar.HOUR; -import static java.util.Calendar.MINUTE; -import static java.util.Calendar.YEAR; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; - -import java.util.GregorianCalendar; -import java.util.Locale; -import java.util.TimeZone; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.Duration; import javax.xml.datatype.XMLGregorianCalendar; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.TimeZone; -import org.testng.Assert; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import static java.util.Calendar.HOUR; +import static java.util.Calendar.MINUTE; +import static java.util.Calendar.YEAR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @bug 5049592 5041845 5048932 5064587 5040542 5049531 5049528 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.datatype.ptests.XMLGregorianCalendarTest + * @run junit/othervm javax.xml.datatype.ptests.XMLGregorianCalendarTest * @summary Class containing the test cases for XMLGregorianCalendar */ public class XMLGregorianCalendarTest { private DatatypeFactory datatypeFactory; - @BeforeClass + @BeforeEach public void setup() throws DatatypeConfigurationException { datatypeFactory = DatatypeFactory.newInstance(); } - @DataProvider(name = "valid-milliseconds") - public Object[][] getValidMilliSeconds() { - return new Object[][] { { 0 }, { 1 }, { 2 }, { 16 }, { 1000 } }; - } - /* * Test DatatypeFactory.newXMLGregorianCalendar(..) with milliseconds > 1. * * Bug # 5049592 - * */ - @Test(dataProvider = "valid-milliseconds") - public void checkNewCalendar(int ms) { + @ParameterizedTest + @ValueSource(ints={ 0, 1, 2, 16, 1000 }) + public void checkNewCalendar(int expectedMillis) { // valid milliseconds XMLGregorianCalendar calendar = datatypeFactory.newXMLGregorianCalendar(2004, // year 6, // month @@ -80,12 +78,12 @@ public class XMLGregorianCalendarTest { 19, // hour 20, // minute 59, // second - ms, // milliseconds + expectedMillis, // milliseconds 840 // timezone ); // expected success - assertEquals(calendar.getMillisecond(), ms); + assertEquals(expectedMillis, calendar.getMillisecond()); } /* @@ -93,23 +91,19 @@ public class XMLGregorianCalendarTest { * * Bug # 5049592 */ - @Test(dataProvider = "valid-milliseconds") - public void checkNewTime(int ms) { + @ParameterizedTest + @ValueSource(ints={ 0, 1, 2, 16, 1000 }) + public void checkNewTime(int expectedMillis) { // valid milliseconds XMLGregorianCalendar calendar2 = datatypeFactory.newXMLGregorianCalendarTime(19, // hour 20, // minute 59, // second - ms, // milliseconds + expectedMillis, // milliseconds 840 // timezone ); // expected success - assertEquals(calendar2.getMillisecond(), ms); - } - - @DataProvider(name = "invalid-milliseconds") - public Object[][] getInvalidMilliSeconds() { - return new Object[][] { { -1 }, { 1001 } }; + assertEquals(expectedMillis, calendar2.getMillisecond()); } /* @@ -119,18 +113,21 @@ public class XMLGregorianCalendarTest { * 1001. * */ - @Test(expectedExceptions = IllegalArgumentException.class, dataProvider = "invalid-milliseconds") - public void checkNewCalendarNeg(int milliseconds) { + @ParameterizedTest + @ValueSource(ints={ -1, 1001 }) + public void checkNewCalendarNeg(int invalidMilliseconds) { // invalid milliseconds - datatypeFactory.newXMLGregorianCalendar(2004, // year - 6, // month - 2, // day - 19, // hour - 20, // minute - 59, // second - milliseconds, // milliseconds - 840 // timezone - ); + assertThrows( + IllegalArgumentException.class, + () -> datatypeFactory.newXMLGregorianCalendar(2004, // year + 6, // month + 2, // day + 19, // hour + 20, // minute + 59, // second + invalidMilliseconds, // milliseconds + 840 // timezone + )); } /* @@ -140,19 +137,21 @@ public class XMLGregorianCalendarTest { * 1001. * */ - @Test(expectedExceptions = IllegalArgumentException.class, dataProvider = "invalid-milliseconds") - public void checkNewTimeNeg(int milliseconds) { + @ParameterizedTest + @ValueSource(ints={ -1, 1001 }) + public void checkNewTimeNeg(int invalidMilliseconds) { // invalid milliseconds - datatypeFactory.newXMLGregorianCalendarTime(19, // hour - 20, // minute - 59, // second - milliseconds, // milliseconds - 840 // timezone - ); + assertThrows( + IllegalArgumentException.class, + () -> datatypeFactory.newXMLGregorianCalendarTime(19, // hour + 20, // minute + 59, // second + invalidMilliseconds, // milliseconds + 840 // timezone + )); } - @DataProvider(name = "data-for-add") - public Object[][] getDataForAdd() { + public static Object[][] getDataForAdd() { return new Object[][] { //calendar1, calendar2, duration { "1999-12-31T00:00:00Z", "2000-01-01T00:00:00Z", "P1D" }, @@ -173,7 +172,8 @@ public class XMLGregorianCalendarTest { * Test XMLGregorianCalendar.add(Duration). * */ - @Test(dataProvider = "data-for-add") + @ParameterizedTest + @MethodSource("getDataForAdd") public void checkAddDays(String cal1, String cal2, String dur) { XMLGregorianCalendar calendar1 = datatypeFactory.newXMLGregorianCalendar(cal1); @@ -184,70 +184,51 @@ public class XMLGregorianCalendarTest { XMLGregorianCalendar calendar1Clone = (XMLGregorianCalendar)calendar1.clone(); calendar1Clone.add(duration); - assertEquals(calendar1Clone, calendar2); + assertEquals(calendar2, calendar1Clone); calendar2.add(duration.negate()); - assertEquals(calendar2, calendar1); + assertEquals(calendar1, calendar2); } - @DataProvider(name = "gMonth") - public Object[][] getGMonth() { - return new Object[][] { - { "2000-02" }, - { "2000-03" }, - { "2018-02" }}; - } /* * Test XMLGregorianCalendar#isValid(). for gMonth * * Bug # 5041845 - * */ - @Test(dataProvider = "gMonth") + @ParameterizedTest + @ValueSource(strings={ "2000-02", "2000-03", "2018-02" }) public void checkIsValid(String month) { - XMLGregorianCalendar gMonth = datatypeFactory.newXMLGregorianCalendar(month); gMonth.setYear(null); - Assert.assertTrue(gMonth.isValid(), gMonth.toString() + " should isValid"); - - } - - @DataProvider(name = "lexical01") - public Object[][] getLexicalRepresentForNormalize01() { - return new Object[][] { { "2000-01-16T12:00:00Z" }, { "2000-01-16T12:00:00" } }; + assertTrue(gMonth.isValid(), gMonth + " should isValid"); } /* * Test XMLGregorianCalendar#normalize(...). * * Bug # 5048932 XMLGregorianCalendar.normalize works - * */ - @Test(dataProvider = "lexical01") + @ParameterizedTest + @ValueSource(strings={ "2000-01-16T12:00:00Z", "2000-01-16T12:00:00" }) public void checkNormalize01(String lexical) { XMLGregorianCalendar lhs = datatypeFactory.newXMLGregorianCalendar(lexical); lhs.normalize(); } - @DataProvider(name = "lexical02") - public Object[][] getLexicalRepresentForNormalize02() { - return new Object[][] { { "2000-01-16T00:00:00.01Z" }, { "2000-01-16T00:00:00.01" }, { "13:20:00" } }; - } - /* * Test XMLGregorianCalendar#normalize(...). * * Bug # 5064587 XMLGregorianCalendar.normalize shall not change timezone - * */ - @Test(dataProvider = "lexical02") + @ParameterizedTest + @ValueSource(strings={ "2000-01-16T00:00:00.01Z", "2000-01-16T00:00:00.01", "13:20:00" }) public void checkNormalize02(String lexical) { XMLGregorianCalendar orig = datatypeFactory.newXMLGregorianCalendar(lexical); XMLGregorianCalendar normalized = datatypeFactory.newXMLGregorianCalendar(lexical).normalize(); - assertEquals(normalized.getTimezone(), orig.getTimezone()); - assertEquals(normalized.getMillisecond(), orig.getMillisecond()); + assertEquals(orig.getTimezone(), normalized.getTimezone()); + assertEquals(orig.getMillisecond(), normalized.getMillisecond()); } /* @@ -291,8 +272,6 @@ public class XMLGregorianCalendarTest { int hour = calendar.get(HOUR); assertTrue((year == 2003 && hour == 2 && minute == 3), " expecting year == 2003, hour == 2, minute == 3" + ", result is year == " + year + ", hour == " + hour + ", minute == " + minute); - - } /* @@ -311,8 +290,7 @@ public class XMLGregorianCalendarTest { calendar.toGregorianCalendar(TimeZone.getDefault(), Locale.getDefault(), null); } - @DataProvider(name = "calendar") - public Object[][] getXMLGregorianCalendarData() { + public static Object[][] getXMLGregorianCalendarData() { return new Object[][] { // year, month, day, hour, minute, second { 1970, 1, 1, 0, 0, 0 }, // DATETIME @@ -332,10 +310,12 @@ public class XMLGregorianCalendarTest { * Bug # 5049528 * */ - @Test(dataProvider = "calendar") + @ParameterizedTest + @MethodSource("getXMLGregorianCalendarData") public void checkToStringPos(final int year, final int month, final int day, final int hour, final int minute, final int second) { XMLGregorianCalendar calendar = datatypeFactory.newXMLGregorianCalendar(year, month, day, hour, minute, second, undef, undef); - calendar.toString(); + assertNotNull(calendar.toString()); + assertFalse(calendar.toString().isEmpty()); } /* @@ -345,13 +325,13 @@ public class XMLGregorianCalendarTest { * if all parameters are undef * */ - @Test(expectedExceptions = IllegalStateException.class) + @Test public void checkToStringNeg() { XMLGregorianCalendar calendar = datatypeFactory.newXMLGregorianCalendar(undef, undef, undef, undef, undef, undef, undef, undef); // expected to fail - calendar.toString(); + assertThrows(IllegalStateException.class, calendar::toString); } - private final int undef = DatatypeConstants.FIELD_UNDEFINED; + private static final int undef = DatatypeConstants.FIELD_UNDEFINED; } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DBFNamespaceTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DBFNamespaceTest.java index 8bf88ff09fc..6cf6e925a36 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DBFNamespaceTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DBFNamespaceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -23,22 +23,21 @@ package javax.xml.parsers.ptests; -import static javax.xml.parsers.ptests.ParserTestConst.GOLDEN_DIR; -import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; - -import java.io.File; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXResult; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.w3c.dom.Document; +import static javax.xml.parsers.ptests.ParserTestConst.GOLDEN_DIR; +import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; /** * This tests DocumentBuilderFactory for namespace processing and no-namespace @@ -47,7 +46,7 @@ import org.w3c.dom.Document; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.parsers.ptests.DBFNamespaceTest + * @run junit/othervm javax.xml.parsers.ptests.DBFNamespaceTest */ public class DBFNamespaceTest { @@ -56,15 +55,14 @@ public class DBFNamespaceTest { * @return a two-dimensional array contains factory, output file name and * golden validate file name. */ - @DataProvider(name = "input-provider") - public Object[][] getInput() { + public static Object[][] getInput() { DocumentBuilderFactory dbf1 = DocumentBuilderFactory.newInstance(); - String outputfile1 = USER_DIR + "dbfnstest01.out"; + String outputfile1 = "dbfnstest01.out"; String goldfile1 = GOLDEN_DIR + "dbfnstest01GF.out"; DocumentBuilderFactory dbf2 = DocumentBuilderFactory.newInstance(); dbf2.setNamespaceAware(true); - String outputfile2 = USER_DIR + "dbfnstest02.out"; + String outputfile2 = "dbfnstest02.out"; String goldfile2 = GOLDEN_DIR + "dbfnstest02GF.out"; return new Object[][] { { dbf1, outputfile1, goldfile1 }, { dbf2, outputfile2, goldfile2 } }; } @@ -77,12 +75,15 @@ public class DBFNamespaceTest { * @param goldfile golden validate file name. * @throws Exception If any errors occur. */ - @Test(dataProvider = "input-provider") + @ParameterizedTest + @MethodSource("getInput") public void testNamespaceTest(DocumentBuilderFactory dbf, String outputfile, String goldfile) throws Exception { Document doc = dbf.newDocumentBuilder().parse(new File(XML_DIR, "namespace1.xml")); dummyTransform(doc, outputfile); - assertTrue(compareWithGold(goldfile, outputfile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldfile)), + Files.readAllLines(Path.of(outputfile))); } /** @@ -90,8 +91,6 @@ public class DBFNamespaceTest { * invoke the callbacks through a ContentHandler. If namespace processing is * not chosen, namespaceURI in callbacks should be an empty string otherwise * it should be namespaceURI. - * - * @throws Exception If any errors occur. */ private void dummyTransform(Document document, String fileName) throws Exception { diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DocumentBuilderFactoryTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DocumentBuilderFactoryTest.java index f21af74330d..044a1ed5de2 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DocumentBuilderFactoryTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DocumentBuilderFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -23,26 +23,16 @@ package javax.xml.parsers.ptests; -import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static javax.xml.parsers.ptests.ParserTestConst.GOLDEN_DIR; -import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static jaxp.library.JAXPTestUtilities.filenameToURL; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertNotSame; - -import java.io.BufferedReader; -import java.io.Closeable; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileReader; +import jaxp.library.JAXPDataProvider; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -54,17 +44,28 @@ import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXResult; +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.nio.file.Files; +import java.nio.file.Path; -import jaxp.library.JAXPDataProvider; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static javax.xml.parsers.ptests.ParserTestConst.GOLDEN_DIR; +import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @bug 8080907 8169778 @@ -73,7 +74,8 @@ import org.xml.sax.helpers.DefaultHandler; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.parsers.ptests.DocumentBuilderFactoryTest + * @build jaxp.library.JAXPDataProvider + * @run junit/othervm javax.xml.parsers.ptests.DocumentBuilderFactoryTest */ public class DocumentBuilderFactoryTest { @@ -93,9 +95,11 @@ public class DocumentBuilderFactoryTest { * * @return a data provider contains DocumentBuilderFactory instantiation parameters. */ - @DataProvider(name = "parameters") - public Object[][] getValidateParameters() { - return new Object[][] { { DOCUMENT_BUILDER_FACTORY_CLASSNAME, null }, { DOCUMENT_BUILDER_FACTORY_CLASSNAME, this.getClass().getClassLoader() } }; + public static Object[][] getValidateParameters() { + return new Object[][] { + { DOCUMENT_BUILDER_FACTORY_CLASSNAME, null }, + { DOCUMENT_BUILDER_FACTORY_CLASSNAME, DocumentBuilderFactoryTest.class.getClassLoader() }, + }; } /** @@ -109,8 +113,8 @@ public class DocumentBuilderFactoryTest { DocumentBuilderFactory dbf2 = DocumentBuilderFactory.newInstance(); assertNotSame(dbf1, dbf2, "same instance returned:"); assertSame(dbf1.getClass(), dbf2.getClass(), - "unexpected class mismatch for newDefaultInstance():"); - assertEquals(dbf1.getClass().getName(), DEFAULT_IMPL_CLASS); + "unexpected class mismatch for newDefaultInstance():"); + assertEquals(DEFAULT_IMPL_CLASS, dbf1.getClass().getName()); } /** @@ -119,12 +123,9 @@ public class DocumentBuilderFactoryTest { * points to correct implementation of * javax.xml.parsers.DocumentBuilderFactory , should return newInstance of * DocumentBuilderFactory - * - * @param factoryClassName - * @param classLoader - * @throws ParserConfigurationException */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("getValidateParameters") public void testNewInstance(String factoryClassName, ClassLoader classLoader) throws ParserConfigurationException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(factoryClassName, classLoader); DocumentBuilder builder = dbf.newDocumentBuilder(); @@ -135,13 +136,13 @@ public class DocumentBuilderFactoryTest { * test for DocumentBuilderFactory.newInstance(java.lang.String * factoryClassName, java.lang.ClassLoader classLoader) factoryClassName is * null , should throw FactoryConfigurationError - * - * @param factoryClassName - * @param classLoader */ - @Test(expectedExceptions = FactoryConfigurationError.class, dataProvider = "new-instance-neg", dataProviderClass = JAXPDataProvider.class) + @ParameterizedTest + @MethodSource("jaxp.library.JAXPDataProvider#newInstanceNeg") public void testNewInstanceNeg(String factoryClassName, ClassLoader classLoader) { - DocumentBuilderFactory.newInstance(factoryClassName, classLoader); + assertThrows( + FactoryConfigurationError.class, + () -> DocumentBuilderFactory.newInstance(factoryClassName, classLoader)); } /** @@ -162,11 +163,11 @@ public class DocumentBuilderFactoryTest { assertFalse(eh.isErrorOccured()); } - @DataProvider(name = "schema-source") - public Object[][] getSchemaSource() throws FileNotFoundException { + public static Object[][] getSchemaSource() throws FileNotFoundException { return new Object[][] { { new FileInputStream(new File(XML_DIR, "test.xsd")) }, - { new InputSource(filenameToURL(XML_DIR + "test.xsd")) } }; + { new InputSource(Path.of(XML_DIR).resolve("test.xsd").toUri().toASCIIString()) }, + }; } /** @@ -174,7 +175,8 @@ public class DocumentBuilderFactoryTest { * this case the schema source property is set. * @throws Exception If any errors occur. */ - @Test(dataProvider = "schema-source") + @ParameterizedTest + @MethodSource("getSchemaSource") public void testCheckSchemaSupport2(Object schemaSource) throws Exception { try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); @@ -201,7 +203,8 @@ public class DocumentBuilderFactoryTest { * this case the schema source property is set. * @throws Exception If any errors occur. */ - @Test(dataProvider = "schema-source") + @ParameterizedTest + @MethodSource("getSchemaSource") public void testCheckSchemaSupport3(Object schemaSource) throws Exception { try { SAXParserFactory spf = SAXParserFactory.newInstance(); @@ -236,7 +239,7 @@ public class DocumentBuilderFactoryTest { Document doc = docBuilder.parse(new File(XML_DIR, "DocumentBuilderFactory01.xml")); Element e = (Element) doc.getElementsByTagName("html").item(0); NodeList nl = e.getChildNodes(); - assertEquals(nl.getLength(), 1); + assertEquals(1, nl.getLength()); } /** @@ -312,7 +315,7 @@ public class DocumentBuilderFactoryTest { Element e = (Element) doc.getElementsByTagName("title").item(0); NodeList nl = e.getChildNodes(); assertTrue(dbf.isExpandEntityReferences()); - assertEquals(nl.item(0).getNodeValue().trim().charAt(0), 'W'); + assertEquals('W', nl.item(0).getNodeValue().trim().charAt(0)); } } @@ -331,7 +334,7 @@ public class DocumentBuilderFactoryTest { MyErrorHandler eh = MyErrorHandler.newInstance(); db.setErrorHandler(eh); Document doc = db.parse(new File(XML_DIR, "DocumentBuilderFactory04.xml")); - assertTrue(doc instanceof Document); + assertInstanceOf(Document.class, doc); assertFalse(eh.isErrorOccured()); } @@ -350,7 +353,7 @@ public class DocumentBuilderFactoryTest { Element e = (Element) doc.getElementsByTagName("title").item(0); NodeList nl = e.getChildNodes(); assertTrue(dbf.isExpandEntityReferences()); - assertEquals(nl.item(0).getNodeValue().trim().charAt(0), 'W'); + assertEquals('W', nl.item(0).getNodeValue().trim().charAt(0)); } } @@ -458,12 +461,12 @@ public class DocumentBuilderFactoryTest { * throw Sax Exception. * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class) + @Test public void testCheckDocumentBuilderFactory14() throws Exception { // Accesing default working directory. DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = dbf.newDocumentBuilder(); - docBuilder.parse(""); + assertThrows(SAXException.class, () -> docBuilder.parse("")); } /** @@ -472,12 +475,12 @@ public class DocumentBuilderFactoryTest { * @throws Exception If any errors occur. * */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testCheckDocumentBuilderFactory15() throws Exception { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = dbf.newDocumentBuilder(); String uri = null; - docBuilder.parse(uri); + assertThrows(IllegalArgumentException.class, () -> docBuilder.parse(uri)); } /** @@ -496,7 +499,7 @@ public class DocumentBuilderFactoryTest { Document doc = docBuilder.parse(fis); Element e = (Element) doc.getElementsByTagName("body").item(0); NodeList nl = e.getChildNodes(); - assertEquals(nl.getLength(), 0); + assertEquals(0, nl.getLength()); } } @@ -531,7 +534,7 @@ public class DocumentBuilderFactoryTest { @Test public void testCheckElementContentWhitespace() throws Exception { String goldFile = GOLDEN_DIR + "dbfactory02GF.out"; - String outputFile = USER_DIR + "dbfactory02.out"; + String outputFile = "dbfactory02.out"; MyErrorHandler eh = MyErrorHandler.newInstance(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setValidating(true); @@ -549,6 +552,8 @@ public class DocumentBuilderFactoryTest { saxResult.setHandler(handler); transformer.transform(domSource, saxResult); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DocumentBuilderImpl01.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DocumentBuilderImpl01.java index 4e9006f9f5b..8d265336bbd 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DocumentBuilderImpl01.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DocumentBuilderImpl01.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -23,23 +23,20 @@ package javax.xml.parsers.ptests; -import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.FILE_SEP; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FilePermission; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import java.io.File; +import java.io.FileInputStream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.xml.sax.EntityResolver; -import org.xml.sax.InputSource; +import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * This checks for the methods of DocumentBuilder @@ -47,7 +44,7 @@ import org.xml.sax.InputSource; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.parsers.ptests.DocumentBuilderImpl01 + * @run junit/othervm javax.xml.parsers.ptests.DocumentBuilderImpl01 */ public class DocumentBuilderImpl01 implements EntityResolver { /** @@ -57,8 +54,7 @@ public class DocumentBuilderImpl01 implements EntityResolver { * @throws ParserConfigurationException if a DocumentBuilder cannot be * created which satisfies the configuration requested. */ - @DataProvider(name = "builder-provider") - public Object[][] getBuilder() throws ParserConfigurationException { + public static Object[][] getBuilder() throws ParserConfigurationException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = dbf.newDocumentBuilder(); return new Object[][] { { docBuilder } }; @@ -69,7 +65,8 @@ public class DocumentBuilderImpl01 implements EntityResolver { * to return false because not setting the validation. * @param docBuilder document builder instance. */ - @Test(dataProvider = "builder-provider") + @ParameterizedTest + @MethodSource("getBuilder") public void testCheckDocumentBuilderImpl01(DocumentBuilder docBuilder) { assertFalse(docBuilder.isValidating()); } @@ -78,7 +75,8 @@ public class DocumentBuilderImpl01 implements EntityResolver { * Test the default functionality of isNamespaceAware method. * @param docBuilder document builder instance. */ - @Test(dataProvider = "builder-provider") + @ParameterizedTest + @MethodSource("getBuilder") public void testCheckDocumentBuilderImpl02(DocumentBuilder docBuilder) { assertFalse(docBuilder.isNamespaceAware()); } @@ -88,7 +86,8 @@ public class DocumentBuilderImpl01 implements EntityResolver { * @param docBuilder document builder instance. * @throws Exception If any errors occur. */ - @Test(dataProvider = "builder-provider") + @ParameterizedTest + @MethodSource("getBuilder") public void testCheckDocumentBuilderImpl04(DocumentBuilder docBuilder) throws Exception { try (FileInputStream fis = new FileInputStream(new File(XML_DIR, @@ -103,7 +102,8 @@ public class DocumentBuilderImpl01 implements EntityResolver { * @param docBuilder document builder instance. * @throws Exception If any errors occur. */ - @Test(dataProvider = "builder-provider") + @ParameterizedTest + @MethodSource("getBuilder") public void testCheckDocumentBuilderImpl05(DocumentBuilder docBuilder) throws Exception { assertNotNull(docBuilder.parse(new File(XML_DIR, @@ -115,13 +115,14 @@ public class DocumentBuilderImpl01 implements EntityResolver { * @param docBuilder document builder instance. * @throws Exception If any errors occur. */ - @Test(dataProvider = "builder-provider") + @ParameterizedTest + @MethodSource("getBuilder") public void testCheckDocumentBuilderImpl06(DocumentBuilder docBuilder) throws Exception { try (FileInputStream fis = new FileInputStream(new File(XML_DIR, "DocumentBuilderImpl02.xml"))) { assertNotNull(docBuilder.parse(fis, new File(XML_DIR).toURI() - .toASCIIString() + FILE_SEP)); + .toASCIIString() + '/')); } } @@ -129,7 +130,8 @@ public class DocumentBuilderImpl01 implements EntityResolver { * Test the setEntityResolver. * @param docBuilder document builder instance. */ - @Test(dataProvider = "builder-provider") + @ParameterizedTest + @MethodSource("getBuilder") public void testCheckDocumentBuilderImpl07(DocumentBuilder docBuilder) { docBuilder.setEntityResolver(this); assertNotNull(resolveEntity("publicId", "http://www.myhost.com/today")); diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/FactoryConfErrorTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/FactoryConfErrorTest.java index 3bcc49d58b2..fe4288fc2b1 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/FactoryConfErrorTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/FactoryConfErrorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -23,16 +23,17 @@ package javax.xml.parsers.ptests; -import static jaxp.library.JAXPTestUtilities.setSystemProperty; -import static jaxp.library.JAXPTestUtilities.clearSystemProperty; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.SAXParserFactory; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Class containing the test cases for SAXParserFactory/DocumentBuilderFactory @@ -41,45 +42,46 @@ import org.testng.annotations.Test; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.parsers.ptests.FactoryConfErrorTest + * @run junit/othervm javax.xml.parsers.ptests.FactoryConfErrorTest */ +@Execution(ExecutionMode.SAME_THREAD) public class FactoryConfErrorTest { /** * Set properties DocumentBuilderFactory and SAXParserFactory to invalid * value before any test run. */ - @BeforeTest - public void setup() { - setSystemProperty("javax.xml.parsers.DocumentBuilderFactory", "xx"); - setSystemProperty("javax.xml.parsers.SAXParserFactory", "xx"); + @BeforeAll + public static void setup() { + System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "xx"); + System.setProperty("javax.xml.parsers.SAXParserFactory", "xx"); } /** * Restore properties DocumentBuilderFactory and SAXParserFactory to default * value after all tests run. */ - @AfterTest - public void cleanup() { - clearSystemProperty("javax.xml.parsers.DocumentBuilderFactory"); - clearSystemProperty("javax.xml.parsers.SAXParserFactory"); + @AfterAll + public static void cleanup() { + System.clearProperty("javax.xml.parsers.DocumentBuilderFactory"); + System.clearProperty("javax.xml.parsers.SAXParserFactory"); } /** * To test exception thrown if javax.xml.parsers.SAXParserFactory property * is invalid. */ - @Test(expectedExceptions = FactoryConfigurationError.class) + @Test public void testNewInstance01() { - SAXParserFactory.newInstance(); + assertThrows(FactoryConfigurationError.class, SAXParserFactory::newInstance); } /** * To test exception thrown if javax.xml.parsers.DocumentBuilderFactory is * invalid. */ - @Test(expectedExceptions = FactoryConfigurationError.class) + @Test public void testNewInstance02() { - DocumentBuilderFactory.newInstance(); + assertThrows(FactoryConfigurationError.class, DocumentBuilderFactory::newInstance); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXFactoryNewInstanceTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXFactoryNewInstanceTest.java index 2b47bd6a3da..22fca5ec207 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXFactoryNewInstanceTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXFactoryNewInstanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -23,32 +23,35 @@ package javax.xml.parsers.ptests; -import static org.testng.Assert.assertNotNull; +import jaxp.library.JAXPDataProvider; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.xml.sax.SAXException; import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; -import jaxp.library.JAXPDataProvider; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.xml.sax.SAXException; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.parsers.ptests.SAXFactoryNewInstanceTest + * @build jaxp.library.JAXPDataProvider + * @run junit/othervm javax.xml.parsers.ptests.SAXFactoryNewInstanceTest * @summary Tests for SAXParserFactory.newInstance(factoryClassName , classLoader) */ public class SAXFactoryNewInstanceTest { private static final String SAXPARSER_FACTORY_CLASSNAME = "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"; - @DataProvider(name = "parameters") - public Object[][] getValidateParameters() { - return new Object[][] { { SAXPARSER_FACTORY_CLASSNAME, null }, { SAXPARSER_FACTORY_CLASSNAME, this.getClass().getClassLoader() } }; + public static Object[][] getValidateParameters() { + return new Object[][] { + { SAXPARSER_FACTORY_CLASSNAME, null }, + { SAXPARSER_FACTORY_CLASSNAME, SAXFactoryNewInstanceTest.class.getClassLoader() }, + }; } /* @@ -57,7 +60,8 @@ public class SAXFactoryNewInstanceTest { * implementation of javax.xml.parsers.SAXParserFactory , should return * newInstance of SAXParserFactory */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("getValidateParameters") public void testNewInstance(String factoryClassName, ClassLoader classLoader) throws ParserConfigurationException, SAXException { SAXParserFactory spf = SAXParserFactory.newInstance(factoryClassName, classLoader); SAXParser sp = spf.newSAXParser(); @@ -69,9 +73,12 @@ public class SAXFactoryNewInstanceTest { * java.lang.ClassLoader classLoader) factoryClassName is null , should * throw FactoryConfigurationError */ - @Test(expectedExceptions = FactoryConfigurationError.class, dataProvider = "new-instance-neg", dataProviderClass = JAXPDataProvider.class) + @ParameterizedTest + @MethodSource("jaxp.library.JAXPDataProvider#newInstanceNeg") public void testNewInstanceNeg(String factoryClassName, ClassLoader classLoader) { - SAXParserFactory.newInstance(factoryClassName, classLoader); + assertThrows( + FactoryConfigurationError.class, + () -> SAXParserFactory.newInstance(factoryClassName, classLoader)); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserFactTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserFactTest.java index 77134c58ac1..0f21dffcea7 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserFactTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserFactTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -22,15 +22,16 @@ */ package javax.xml.parsers.ptests; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertNotSame; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.Test; import javax.xml.parsers.SAXParserFactory; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Class containing the test cases for SAXParserFactory API. @@ -39,7 +40,7 @@ import org.testng.annotations.Test; * @test * @bug 8169778 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.parsers.ptests.SAXParserFactTest + * @run junit/othervm javax.xml.parsers.ptests.SAXParserFactTest */ public class SAXParserFactTest { @@ -55,16 +56,15 @@ public class SAXParserFactTest { /** * Test if newDefaultInstance() method returns an instance * of the expected factory. - * @throws Exception If any errors occur. */ @Test - public void testDefaultInstance() throws Exception { + public void testDefaultInstance() { SAXParserFactory spf1 = SAXParserFactory.newDefaultInstance(); SAXParserFactory spf2 = SAXParserFactory.newInstance(); assertNotSame(spf1, spf2, "same instance returned:"); assertSame(spf1.getClass(), spf2.getClass(), - "unexpected class mismatch for newDefaultInstance():"); - assertEquals(spf1.getClass().getName(), DEFAULT_IMPL_CLASS); + "unexpected class mismatch for newDefaultInstance():"); + assertEquals(DEFAULT_IMPL_CLASS, spf1.getClass().getName()); } /** diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest.java index f828dbeea18..38f2e98e286 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -23,37 +23,45 @@ package javax.xml.parsers.ptests; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.PropertyPermission; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; -import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.xml.sax.HandlerBase; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertThrows; + /** * Class contains the test cases for SAXParser API */ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.parsers.ptests.SAXParserTest + * @run junit/othervm javax.xml.parsers.ptests.SAXParserTest */ public class SAXParserTest { + private static final DefaultHandler DEFAULT_HANDLER = new DefaultHandler(); + + @SuppressWarnings("deprecation") + private static final HandlerBase HANDLER_BASE = new HandlerBase(); + /** * Provide SAXParser. * * @return a data provider contains a SAXParser instance. * @throws Exception If any errors occur. */ - @DataProvider(name = "parser-provider") - public Object[][] getParser() throws Exception { + public static Object[][] getParser() throws Exception { SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser saxparser = spf.newSAXParser(); return new Object[][] { { saxparser } }; @@ -62,423 +70,332 @@ public class SAXParserTest { /** * Test case with FileInputStream null, parsing should fail and throw * IllegalArgumentException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class, - dataProvider = "parser-provider") - public void testParse01(SAXParser saxparser) throws Exception { - FileInputStream instream = null; - saxparser.parse(instream, new HandlerBase()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse01(SAXParser saxparser) { + assertThrows(IllegalArgumentException.class, () -> saxparser.parse((InputStream) null, HANDLER_BASE)); } /** * Test with by setting URI as null, parsing should fail and throw * IllegalArgumentException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class, - dataProvider = "parser-provider") - public void testParse02(SAXParser saxparser) throws Exception { - String uri = null; - saxparser.parse(uri, new HandlerBase()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse02(SAXParser saxparser) { + assertThrows(IllegalArgumentException.class, () -> saxparser.parse((String) null, HANDLER_BASE)); } /** * Test with non-existence URI, parsing should fail and throw IOException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = { SAXException.class }, - dataProvider = "parser-provider") - public void testParse03(SAXParser saxparser) throws Exception { - saxparser.parse("", new HandlerBase()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse03(SAXParser saxparser) { + assertThrows(SAXException.class, () -> saxparser.parse("", HANDLER_BASE)); } /** * Test with File null, parsing should fail and throw * IllegalArgumentException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class, - dataProvider = "parser-provider") - public void testParse04(SAXParser saxparser) throws Exception { - File file = null; - saxparser.parse(file, new HandlerBase()); + public void testParse04(SAXParser saxparser) { + assertThrows(IllegalArgumentException.class, () -> saxparser.parse((File) null, HANDLER_BASE)); } /** * Test with empty string as File, parsing should fail and throw * SAXException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class, dataProvider = "parser-provider") - public void testParse05(SAXParser saxparser) throws Exception { - saxparser.parse(new File(""), new HandlerBase()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse05(SAXParser saxparser) { + File file = new File(""); + assertThrows(SAXException.class, () -> saxparser.parse(file, HANDLER_BASE)); } /** * Test with input source null, parsing should fail and throw * IllegalArgumentException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class, - dataProvider = "parser-provider") - public void testParse06(SAXParser saxparser) throws Exception { - InputSource is = null; - saxparser.parse(is, new HandlerBase()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse06(SAXParser saxparser) { + assertThrows(IllegalArgumentException.class, () -> saxparser.parse((InputSource) null, HANDLER_BASE)); } /** * Test with FileInputStream null, parsing should fail and throw * IllegalArgumentException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class, - dataProvider = "parser-provider") - public void testParse07(SAXParser saxparser) throws Exception { - FileInputStream instream = null; - saxparser.parse(instream, new DefaultHandler()); + public void testParse07(SAXParser saxparser) { + assertThrows(IllegalArgumentException.class, () -> saxparser.parse((InputStream) null, DEFAULT_HANDLER)); } /** * Test with URI null, parsing should fail and throw * IllegalArgumentException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class, - dataProvider = "parser-provider") - public void testParse08(SAXParser saxparser) throws Exception { - String uri = null; - saxparser.parse(uri, new DefaultHandler()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse08(SAXParser saxparser) { + assertThrows(IllegalArgumentException.class, () -> saxparser.parse((String) null, DEFAULT_HANDLER)); } /** * Test with non-existence URI, parsing should fail and throw SAXException * or IOException. - * - * @param saxparser - * a SAXParser instance. - * @throws Exception - * If any errors occur. */ - @Test(expectedExceptions = { SAXException.class, IOException.class }, dataProvider = "parser-provider") - public void testParse09(SAXParser saxparser) throws Exception { - saxparser.parse("no-such-file", new DefaultHandler()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse09(SAXParser saxparser) { + assertThrows(IOException.class, () -> saxparser.parse("no-such-file", DEFAULT_HANDLER)); } /** * Test with empty string as File, parsing should fail and throw * SAXException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class, dataProvider = "parser-provider") - public void testParse10(SAXParser saxparser) throws Exception { + @ParameterizedTest + @MethodSource("getParser") + public void testParse10(SAXParser saxparser) { File file = new File(""); - saxparser.parse(file, new DefaultHandler()); + assertThrows(SAXException.class, () -> saxparser.parse(file, DEFAULT_HANDLER)); } /** * Test with File null, parsing should fail and throw * IllegalArgumentException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class, - dataProvider = "parser-provider") - public void testParse11(SAXParser saxparser) throws Exception { - saxparser.parse((File) null, new DefaultHandler()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse11(SAXParser saxparser) { + assertThrows(IllegalArgumentException.class, () -> saxparser.parse((File) null, DEFAULT_HANDLER)); } /** * Test with input source null, parsing should fail and throw * IllegalArgumentException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class, - dataProvider = "parser-provider") - public void testParse12(SAXParser saxparser) throws Exception { - InputSource is = null; - saxparser.parse(is, new DefaultHandler()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse12(SAXParser saxparser) { + assertThrows(IllegalArgumentException.class, () -> saxparser.parse((InputSource) null, DEFAULT_HANDLER)); } /** * Test with an error in XML file, parsing should fail and throw * SAXException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class, - dataProvider = "parser-provider") public void testParse13(SAXParser saxparser) throws Exception { try (FileInputStream instream = new FileInputStream(new File( XML_DIR, "invalid.xml"))) { - saxparser.parse(instream, new HandlerBase()); + assertThrows(SAXException.class, () -> saxparser.parse(instream, HANDLER_BASE)); } } /** * Test with a valid in XML file, parser should parse the XML document. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse14(SAXParser saxparser) throws Exception { - saxparser.parse(new File(XML_DIR, "parsertest.xml"), - new HandlerBase()); + saxparser.parse(new File(XML_DIR, "parsertest.xml"), HANDLER_BASE); } /** * Test with valid input stream, parser should parse the XML document * successfully. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse15(SAXParser saxparser) throws Exception { - try (FileInputStream instream = new FileInputStream(new File(XML_DIR, - "correct.xml"))) { - saxparser.parse(instream, new HandlerBase()); + try (FileInputStream instream = new FileInputStream(new File(XML_DIR, "correct.xml"))) { + saxparser.parse(instream, HANDLER_BASE); } } /** * Test with valid input source, parser should parse the XML document * successfully. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse16(SAXParser saxparser) throws Exception { try (FileInputStream instream = new FileInputStream( new File(XML_DIR, "parsertest.xml"))) { - saxparser.parse(instream, new HandlerBase(), + saxparser.parse(instream, HANDLER_BASE, new File(XML_DIR).toURI().toASCIIString()); } } /** * Test with proper URI, parser should parse successfully. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse17(SAXParser saxparser) throws Exception { File file = new File(XML_DIR, "correct.xml"); - saxparser.parse(file.toURI().toASCIIString(), new HandlerBase()); + saxparser.parse(file.toURI().toASCIIString(), HANDLER_BASE); } /** * Test with XML file that has errors parsing should fail and throw * SAXException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class, - dataProvider = "parser-provider") - public void testParse18(SAXParser saxparser) throws Exception { - saxparser.parse(new File(XML_DIR, "valid.xml"), new HandlerBase()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse18(SAXParser saxparser) { + File file = new File(XML_DIR, "valid.xml"); + assertThrows(SAXException.class, () -> saxparser.parse(file, HANDLER_BASE)); } /** * Test with XML file that has no errors Parser should successfully * parse the XML document. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse19(SAXParser saxparser) throws Exception { - saxparser.parse(new File(XML_DIR, "correct.xml"), new HandlerBase()); + saxparser.parse(new File(XML_DIR, "correct.xml"), HANDLER_BASE); } /** * Test with input source attached an invalid XML, parsing should fail * and throw SAXException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class, - dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse20(SAXParser saxparser) throws Exception { try(FileInputStream instream = new FileInputStream(new File(XML_DIR, "invalid.xml"))) { - saxparser.parse(new InputSource(instream), new HandlerBase()); + InputSource is = new InputSource(instream); + assertThrows(SAXException.class, () -> saxparser.parse(is, HANDLER_BASE)); } } /** * Test with input source attached an valid XML, parser should * successfully parse the XML document. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse21(SAXParser saxparser) throws Exception { try (FileInputStream instream = new FileInputStream(new File(XML_DIR, "correct.xml"))) { - saxparser.parse(new InputSource(instream), new HandlerBase()); + saxparser.parse(new InputSource(instream), HANDLER_BASE); } } /** * Test with an error in xml file, parsing should fail and throw * SAXException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class, - dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse22(SAXParser saxparser) throws Exception { try (FileInputStream instream = new FileInputStream( new File(XML_DIR, "invalid.xml"))) { - saxparser.parse(instream, new DefaultHandler()); + assertThrows(SAXException.class, () -> saxparser.parse(instream, DEFAULT_HANDLER)); } } /** * Test with valid input stream, parser should parse the XML document * successfully. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse23(SAXParser saxparser) throws Exception { - DefaultHandler handler = new DefaultHandler(); - saxparser.parse(new File(XML_DIR, "parsertest.xml"), handler); + saxparser.parse(new File(XML_DIR, "parsertest.xml"), DEFAULT_HANDLER); } /** * Test with valid input stream, parser should parse the XML document * successfully. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse24(SAXParser saxparser) throws Exception { try (FileInputStream instream = new FileInputStream(new File(XML_DIR, "correct.xml"))) { - DefaultHandler handler = new DefaultHandler(); - saxparser.parse(instream, handler); + saxparser.parse(instream, DEFAULT_HANDLER); } } /** * Test with valid input source, parser should parse the XML document * successfully. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse25(SAXParser saxparser) throws Exception { try (FileInputStream instream = new FileInputStream( new File(XML_DIR, "parsertest.xml"))) { - saxparser.parse(instream, new DefaultHandler(), - new File(XML_DIR).toURI().toASCIIString()); + saxparser.parse(instream, DEFAULT_HANDLER, + new File(XML_DIR).toURI().toASCIIString()); } } /** * Test with proper URI, parser should parse successfully. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse26(SAXParser saxparser) throws Exception { File file = new File(XML_DIR, "correct.xml"); - saxparser.parse(file.toURI().toASCIIString(), new DefaultHandler()); + saxparser.parse(file.toURI().toASCIIString(), DEFAULT_HANDLER); } /** * Test with XML file that has errors, parsing should fail and throw * SAXException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class, - dataProvider = "parser-provider") - public void testParse27(SAXParser saxparser) throws Exception { - saxparser.parse(new File(XML_DIR, "valid.xml"), new DefaultHandler()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse27(SAXParser saxparser) { + File file = new File(XML_DIR, "valid.xml"); + assertThrows(SAXException.class, () -> saxparser.parse(file, DEFAULT_HANDLER)); } /** * Test with XML file that has no errors, parser should successfully * parse the XML document. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse28(SAXParser saxparser) throws Exception { - saxparser.parse(new File(XML_DIR, "correct.xml"), new DefaultHandler()); + saxparser.parse(new File(XML_DIR, "correct.xml"), DEFAULT_HANDLER); } /** * Test with an invalid XML file, parser should throw SAXException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class, - dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse29(SAXParser saxparser) throws Exception { try (FileInputStream instream = new FileInputStream( new File(XML_DIR, "invalid.xml"))) { - saxparser.parse(new InputSource(instream), new DefaultHandler()); + InputSource is = new InputSource(instream); + assertThrows(SAXException.class, () -> saxparser.parse(is, DEFAULT_HANDLER)); } } /** * Test case to parse an XML file that not use namespaces. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse30(SAXParser saxparser) throws Exception { try (FileInputStream instream = new FileInputStream( new File(XML_DIR, "correct.xml"))) { - saxparser.parse(new InputSource(instream), new DefaultHandler()); + saxparser.parse(new InputSource(instream), DEFAULT_HANDLER); } } /** * Test case to parse an XML file that uses namespaces. - * - * @throws Exception If any errors occur. */ @Test public void testParse31() throws Exception { @@ -486,7 +403,7 @@ public class SAXParserTest { new File(XML_DIR, "ns4.xml"))) { SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setNamespaceAware(true); - spf.newSAXParser().parse(instream, new HandlerBase()); + spf.newSAXParser().parse(instream, HANDLER_BASE); } } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest02.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest02.java index db02034e22c..2597bd975a5 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest02.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest02.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -23,28 +23,31 @@ package javax.xml.parsers.ptests; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; - -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.xml.sax.SAXException; import org.xml.sax.SAXNotSupportedException; import org.xml.sax.ext.DeclHandler; import org.xml.sax.ext.LexicalHandler; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + /** * Class contains the test cases for SAXParser API */ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.parsers.ptests.SAXParserTest02 + * @run junit/othervm javax.xml.parsers.ptests.SAXParserTest02 */ public class SAXParserTest02 { private static final String DOM_NODE = "http://xml.org/sax/properties/dom-node"; @@ -58,8 +61,7 @@ public class SAXParserTest02 { * @return a data provider contains a SAXParser instance. * @throws Exception If any errors occur. */ - @DataProvider(name = "parser-provider") - public Object[][] getParser() throws Exception { + public static Object[][] getParser() throws Exception { SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser saxparser = spf.newSAXParser(); return new Object[][] { { saxparser } }; @@ -70,7 +72,8 @@ public class SAXParserTest02 { * * @param saxparser a SAXParser instance. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testValidate01(SAXParser saxparser) { assertFalse(saxparser.isValidating()); } @@ -78,8 +81,6 @@ public class SAXParserTest02 { /** * Test to test the functionality of setValidating and isValidating * methods. - * - * @throws Exception If any errors occur. */ @Test public void testValidate02() throws Exception { @@ -95,7 +96,8 @@ public class SAXParserTest02 { * * @param saxparser a SAXParser instance. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testNamespace01(SAXParser saxparser) { assertFalse(saxparser.isNamespaceAware()); } @@ -119,7 +121,8 @@ public class SAXParserTest02 { * @param saxparser a SAXParser instance. * @throws SAXException If any parse errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParser01(SAXParser saxparser) throws SAXException { assertNotNull(saxparser.getParser()); } @@ -131,7 +134,8 @@ public class SAXParserTest02 { * @param saxparser a SAXParser instance. * @throws SAXException If any parse errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testXmlReader01(SAXParser saxparser) throws SAXException { assertNotNull(saxparser.getXMLReader()); } @@ -142,10 +146,10 @@ public class SAXParserTest02 { * @param saxparser a SAXParser instance. * @throws SAXException If any parse errors occur. */ - @Test(expectedExceptions = SAXNotSupportedException.class, - dataProvider = "parser-provider") - public void testProperty01(SAXParser saxparser) throws SAXException { - saxparser.getProperty(XML_STRING); + @ParameterizedTest + @MethodSource("getParser") + public void testProperty01(SAXParser saxparser) { + assertThrows(SAXNotSupportedException.class, () -> saxparser.getProperty(XML_STRING)); } /** @@ -154,10 +158,10 @@ public class SAXParserTest02 { * @param saxparser a SAXParser instance. * @throws SAXException If any parse errors occur. */ - @Test(expectedExceptions = SAXNotSupportedException.class, - dataProvider = "parser-provider") - public void testProperty02(SAXParser saxparser) throws SAXException { - saxparser.getProperty(DOM_NODE); + @ParameterizedTest + @MethodSource("getParser") + public void testProperty02(SAXParser saxparser) { + assertThrows(SAXNotSupportedException.class, () -> saxparser.getProperty(DOM_NODE)); } /** @@ -166,7 +170,8 @@ public class SAXParserTest02 { * @param saxparser a SAXParser instance. * @throws SAXException If any parse errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testProperty03(SAXParser saxparser) throws SAXException { assertNull(saxparser.getProperty(LEXICAL_HANDLER)); } @@ -177,7 +182,8 @@ public class SAXParserTest02 { * @param saxparser a SAXParser instance. * @throws SAXException If any parse errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testProperty04(SAXParser saxparser) throws SAXException { assertNull(saxparser.getProperty(DECL_HANDLER)); } @@ -188,11 +194,12 @@ public class SAXParserTest02 { * @param saxparser a SAXParser instance. * @throws SAXException If any parse errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testProperty05(SAXParser saxparser) throws SAXException { MyLexicalHandler myLexicalHandler = new MyLexicalHandler(); saxparser.setProperty(LEXICAL_HANDLER, myLexicalHandler); - assertTrue(saxparser.getProperty(LEXICAL_HANDLER) instanceof LexicalHandler); + assertInstanceOf(LexicalHandler.class, saxparser.getProperty(LEXICAL_HANDLER)); } /** @@ -201,11 +208,12 @@ public class SAXParserTest02 { * @param saxparser a SAXParser instance. * @throws SAXException If any parse errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testProperty06(SAXParser saxparser) throws SAXException { MyDeclHandler myDeclHandler = new MyDeclHandler(); saxparser.setProperty(DECL_HANDLER, myDeclHandler); - assertTrue(saxparser.getProperty(DECL_HANDLER) instanceof DeclHandler); + assertInstanceOf(DeclHandler.class, saxparser.getProperty(DECL_HANDLER)); } /** diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest03.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest03.java index 47d202da7b8..61a270b67d5 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest03.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest03.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -23,19 +23,18 @@ package javax.xml.parsers.ptests; -import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; - -import java.io.File; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.xml.sax.SAXException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +import java.io.File; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.xml.sax.SAXException; +import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Class contains the test cases for SAXParser API @@ -43,7 +42,7 @@ import org.xml.sax.SAXException; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.parsers.ptests.SAXParserTest03 + * @run junit/othervm javax.xml.parsers.ptests.SAXParserTest03 */ public class SAXParserTest03 { @@ -52,8 +51,7 @@ public class SAXParserTest03 { * * @return a dimensional contains. */ - @DataProvider(name = "input-provider") - public Object[][] getFactory() { + public static Object[][] getFactory() { SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setValidating(true); return new Object[][] { { spf, MyErrorHandler.newInstance() } }; @@ -67,7 +65,8 @@ public class SAXParserTest03 { * @param handler an error handler for capturing events. * @throws Exception If any errors occur. */ - @Test(dataProvider = "input-provider") + @ParameterizedTest + @MethodSource("getFactory") public void testParseValidate01(SAXParserFactory spf, MyErrorHandler handler) throws Exception { spf.newSAXParser().parse(new File(XML_DIR, "parsertest.xml"), handler); @@ -82,7 +81,8 @@ public class SAXParserTest03 { * @param handler an error handler for capturing events. * @throws Exception If any errors occur. */ - @Test(dataProvider = "input-provider") + @ParameterizedTest + @MethodSource("getFactory") public void testParseValidate02(SAXParserFactory spf, MyErrorHandler handler) throws Exception { spf.setNamespaceAware(true); @@ -99,17 +99,13 @@ public class SAXParserTest03 { * @param handler an error handler for capturing events. * @throws Exception If any errors occur. */ - @Test(dataProvider = "input-provider") + @ParameterizedTest + @MethodSource("getFactory") public void testParseValidate03(SAXParserFactory spf, MyErrorHandler handler) throws Exception { - try { - spf.setNamespaceAware(true); - SAXParser saxparser = spf.newSAXParser(); - saxparser.parse(new File(XML_DIR, "invalidns.xml"), handler); - fail("Expecting SAXException here"); - } catch (SAXException e) { - assertTrue(handler.isErrorOccured()); - } + spf.setNamespaceAware(true); + SAXParser saxparser = spf.newSAXParser(); + assertThrows(SAXException.class, () -> saxparser.parse(new File(XML_DIR, "invalidns.xml"), handler)); + assertTrue(handler.isErrorOccured()); } - } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLEventFactoryNewInstanceTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLEventFactoryNewInstanceTest.java index b846524e0bf..36bf2d01c36 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLEventFactoryNewInstanceTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLEventFactoryNewInstanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -23,26 +23,25 @@ package javax.xml.stream.ptests; -import static jaxp.library.JAXPTestUtilities.setSystemProperty; -import static jaxp.library.JAXPTestUtilities.clearSystemProperty; - -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNotSame; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertEquals; +import jaxp.library.JAXPDataProvider; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import javax.xml.stream.XMLEventFactory; -import jaxp.library.JAXPDataProvider; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @bug 8169778 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.stream.ptests.XMLEventFactoryNewInstanceTest + * @build jaxp.library.JAXPDataProvider + * @run junit/othervm javax.xml.stream.ptests.XMLEventFactoryNewInstanceTest * @summary Tests for XMLEventFactory.newFactory(factoryId , classLoader) */ public class XMLEventFactoryNewInstanceTest { @@ -52,26 +51,25 @@ public class XMLEventFactoryNewInstanceTest { private static final String XMLEVENT_FACTORY_CLASSNAME = DEFAULT_IMPL_CLASS; private static final String XMLEVENT_FACTORY_ID = "javax.xml.stream.XMLEventFactory"; - @DataProvider(name = "parameters") - public Object[][] getValidateParameters() { + public static Object[][] getValidateParameters() { return new Object[][] { - { XMLEVENT_FACTORY_ID, null }, - { XMLEVENT_FACTORY_ID, this.getClass().getClassLoader() } }; + { XMLEVENT_FACTORY_ID, null }, + { XMLEVENT_FACTORY_ID, XMLEventFactoryNewInstanceTest.class.getClassLoader() }, + }; } /** * Test if newDefaultFactory() method returns an instance * of the expected factory. - * @throws Exception If any errors occur. */ @Test - public void testDefaultInstance() throws Exception { + public void testDefaultInstance() { XMLEventFactory ef1 = XMLEventFactory.newDefaultFactory(); XMLEventFactory ef2 = XMLEventFactory.newFactory(); assertNotSame(ef1, ef2, "same instance returned:"); assertSame(ef1.getClass(), ef2.getClass(), - "unexpected class mismatch for newDefaultFactory():"); - assertEquals(ef1.getClass().getName(), DEFAULT_IMPL_CLASS); + "unexpected class mismatch for newDefaultFactory():"); + assertEquals(DEFAULT_IMPL_CLASS, ef1.getClass().getName()); } /* @@ -80,14 +78,15 @@ public class XMLEventFactoryNewInstanceTest { * implementation of javax.xml.stream.XMLEventFactory , should return * newInstance of XMLEventFactory */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("getValidateParameters") public void testNewFactory(String factoryId, ClassLoader classLoader) { - setSystemProperty(XMLEVENT_FACTORY_ID, XMLEVENT_FACTORY_CLASSNAME); + System.setProperty(XMLEVENT_FACTORY_ID, XMLEVENT_FACTORY_CLASSNAME); try { XMLEventFactory xef = XMLEventFactory.newFactory(factoryId, classLoader); assertNotNull(xef); } finally { - clearSystemProperty(XMLEVENT_FACTORY_ID); + System.clearProperty(XMLEVENT_FACTORY_ID); } } @@ -96,9 +95,10 @@ public class XMLEventFactoryNewInstanceTest { * java.lang.ClassLoader classLoader) factoryClassName is null , should * throw NullPointerException */ - @Test(expectedExceptions = NullPointerException.class, dataProvider = "new-instance-neg", dataProviderClass = JAXPDataProvider.class) + @ParameterizedTest + @MethodSource("jaxp.library.JAXPDataProvider#newInstanceNeg") public void testNewFactoryNeg(String factoryId, ClassLoader classLoader) { - XMLEventFactory.newFactory(null, null); + assertThrows(NullPointerException.class, () -> XMLEventFactory.newFactory(factoryId, classLoader)); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLInputFactoryNewInstanceTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLInputFactoryNewInstanceTest.java index ac6b009410c..fe1ef8d746e 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLInputFactoryNewInstanceTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLInputFactoryNewInstanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -23,26 +23,25 @@ package javax.xml.stream.ptests; -import static jaxp.library.JAXPTestUtilities.setSystemProperty; -import static jaxp.library.JAXPTestUtilities.clearSystemProperty; - -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNotSame; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertEquals; +import jaxp.library.JAXPDataProvider; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import javax.xml.stream.XMLInputFactory; -import jaxp.library.JAXPDataProvider; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @bug 8169778 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.stream.ptests.XMLInputFactoryNewInstanceTest + * @build jaxp.library.JAXPDataProvider + * @run junit/othervm javax.xml.stream.ptests.XMLInputFactoryNewInstanceTest * @summary Tests for XMLInputFactory.newFactory(factoryId , classLoader) */ public class XMLInputFactoryNewInstanceTest { @@ -52,26 +51,25 @@ public class XMLInputFactoryNewInstanceTest { private static final String XMLINPUT_FACTORY_CLASSNAME = DEFAULT_IMPL_CLASS; private static final String XMLINPUT_FACTORY_ID = "javax.xml.stream.XMLInputFactory"; - @DataProvider(name = "parameters") - public Object[][] getValidateParameters() { + public static Object[][] getValidateParameters() { return new Object[][] { - { XMLINPUT_FACTORY_ID, null }, - { XMLINPUT_FACTORY_ID, this.getClass().getClassLoader() } }; + { XMLINPUT_FACTORY_ID, null }, + { XMLINPUT_FACTORY_ID, XMLInputFactoryNewInstanceTest.class.getClassLoader() }, + }; } /** * Test if newDefaultFactory() method returns an instance * of the expected factory. - * @throws Exception If any errors occur. */ @Test - public void testDefaultInstance() throws Exception { + public void testDefaultInstance() { XMLInputFactory if1 = XMLInputFactory.newDefaultFactory(); XMLInputFactory if2 = XMLInputFactory.newFactory(); assertNotSame(if1, if2, "same instance returned:"); assertSame(if1.getClass(), if2.getClass(), - "unexpected class mismatch for newDefaultFactory():"); - assertEquals(if1.getClass().getName(), DEFAULT_IMPL_CLASS); + "unexpected class mismatch for newDefaultFactory():"); + assertEquals(DEFAULT_IMPL_CLASS, if1.getClass().getName()); } /* @@ -80,14 +78,15 @@ public class XMLInputFactoryNewInstanceTest { * implementation of javax.xml.stream.XMLInputFactory , should return * newInstance of XMLInputFactory */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("getValidateParameters") public void testNewFactory(String factoryId, ClassLoader classLoader) { - setSystemProperty(XMLINPUT_FACTORY_ID, XMLINPUT_FACTORY_CLASSNAME); + System.setProperty(XMLINPUT_FACTORY_ID, XMLINPUT_FACTORY_CLASSNAME); try { XMLInputFactory xif = XMLInputFactory.newFactory(factoryId, classLoader); assertNotNull(xif); } finally { - clearSystemProperty(XMLINPUT_FACTORY_ID); + System.clearProperty(XMLINPUT_FACTORY_ID); } } @@ -96,9 +95,10 @@ public class XMLInputFactoryNewInstanceTest { * java.lang.ClassLoader classLoader) factoryClassName is null , should * throw NullPointerException */ - @Test(expectedExceptions = NullPointerException.class, dataProvider = "new-instance-neg", dataProviderClass = JAXPDataProvider.class) + @ParameterizedTest + @MethodSource("jaxp.library.JAXPDataProvider#newInstanceNeg") public void testNewFactoryNeg(String factoryId, ClassLoader classLoader) { - XMLInputFactory.newFactory(factoryId, classLoader); + assertThrows(NullPointerException.class, () -> XMLInputFactory.newFactory(factoryId, classLoader)); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLOutputFactoryNewInstanceTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLOutputFactoryNewInstanceTest.java index fa406024834..b531759f0f7 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLOutputFactoryNewInstanceTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLOutputFactoryNewInstanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -23,26 +23,25 @@ package javax.xml.stream.ptests; -import static jaxp.library.JAXPTestUtilities.setSystemProperty; -import static jaxp.library.JAXPTestUtilities.clearSystemProperty; - -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNotSame; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertEquals; +import jaxp.library.JAXPDataProvider; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import javax.xml.stream.XMLOutputFactory; -import jaxp.library.JAXPDataProvider; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @bug 8169778 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.stream.ptests.XMLOutputFactoryNewInstanceTest + * @build jaxp.library.JAXPDataProvider + * @run junit/othervm javax.xml.stream.ptests.XMLOutputFactoryNewInstanceTest * @summary Tests for XMLOutputFactory.newFactory(factoryId , classLoader) */ public class XMLOutputFactoryNewInstanceTest { @@ -52,26 +51,25 @@ public class XMLOutputFactoryNewInstanceTest { private static final String XMLOUTPUT_FACTORY_CLASSNAME = DEFAULT_IMPL_CLASS; private static final String XMLOUTPUT_FACTORY_ID = "javax.xml.stream.XMLOutputFactory"; - @DataProvider(name = "parameters") - public Object[][] getValidateParameters() { + public static Object[][] getValidateParameters() { return new Object[][] { - { XMLOUTPUT_FACTORY_ID, null }, - { XMLOUTPUT_FACTORY_ID, this.getClass().getClassLoader() } }; + { XMLOUTPUT_FACTORY_ID, null }, + { XMLOUTPUT_FACTORY_ID, XMLOutputFactoryNewInstanceTest.class.getClassLoader() }, + }; } /** * Test if newDefaultFactory() method returns an instance * of the expected factory. - * @throws Exception If any errors occur. */ @Test - public void testDefaultInstance() throws Exception { + public void testDefaultInstance() { XMLOutputFactory of1 = XMLOutputFactory.newDefaultFactory(); XMLOutputFactory of2 = XMLOutputFactory.newFactory(); assertNotSame(of1, of2, "same instance returned:"); assertSame(of1.getClass(), of2.getClass(), - "unexpected class mismatch for newDefaultFactory():"); - assertEquals(of1.getClass().getName(), DEFAULT_IMPL_CLASS); + "unexpected class mismatch for newDefaultFactory():"); + assertEquals(DEFAULT_IMPL_CLASS, of1.getClass().getName()); } /* @@ -80,14 +78,15 @@ public class XMLOutputFactoryNewInstanceTest { * implementation of javax.xml.stream.XMLOutputFactory , should return * newInstance of XMLOutputFactory */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("getValidateParameters") public void testNewFactory(String factoryId, ClassLoader classLoader) { - setSystemProperty(XMLOUTPUT_FACTORY_ID, XMLOUTPUT_FACTORY_CLASSNAME); + System.setProperty(XMLOUTPUT_FACTORY_ID, XMLOUTPUT_FACTORY_CLASSNAME); try { XMLOutputFactory xif = XMLOutputFactory.newFactory(factoryId, classLoader); assertNotNull(xif); } finally { - clearSystemProperty(XMLOUTPUT_FACTORY_ID); + System.clearProperty(XMLOUTPUT_FACTORY_ID); } } @@ -96,9 +95,10 @@ public class XMLOutputFactoryNewInstanceTest { * java.lang.ClassLoader classLoader) factoryClassName is null , should * throw NullPointerException */ - @Test(expectedExceptions = NullPointerException.class, dataProvider = "new-instance-neg", dataProviderClass = JAXPDataProvider.class) + @ParameterizedTest + @MethodSource("jaxp.library.JAXPDataProvider#newInstanceNeg") public void testNewFactoryNeg(String factoryId, ClassLoader classLoader) { - XMLOutputFactory.newFactory(factoryId, classLoader); + assertThrows(NullPointerException.class, () -> XMLOutputFactory.newFactory(factoryId, classLoader)); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/Bug6384418Test.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/Bug6384418Test.java index cbe4cc59209..5023d675093 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/Bug6384418Test.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/Bug6384418Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -23,10 +23,8 @@ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; - -import java.io.ByteArrayOutputStream; -import java.io.File; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -35,15 +33,16 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import java.io.ByteArrayOutputStream; +import java.io.File; -import org.testng.annotations.Test; -import org.w3c.dom.Document; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; /* * @test * @bug 6384418 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.Bug6384418Test + * @run junit/othervm javax.xml.transform.ptests.Bug6384418Test * @summary verify the transforming won't throw any exception */ public class Bug6384418Test { diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/DOMResultTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/DOMResultTest.java index b26537d22bf..9229c3feb10 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/DOMResultTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/DOMResultTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -23,23 +23,7 @@ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; - -import java.io.BufferedWriter; -import java.io.FileWriter; -import java.io.IOException; - -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMResult; -import javax.xml.transform.sax.SAXSource; -import javax.xml.transform.sax.SAXTransformerFactory; -import javax.xml.transform.sax.TransformerHandler; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Attr; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; @@ -48,6 +32,21 @@ import org.xml.sax.InputSource; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderFactory; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.sax.TransformerHandler; +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; + /** * DOM parse on test file to be compared with golden output file. No Exception * is expected. @@ -55,7 +54,7 @@ import org.xml.sax.helpers.XMLReaderFactory; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.DOMResultTest + * @run junit/othervm javax.xml.transform.ptests.DOMResultTest */ public class DOMResultTest { /** @@ -64,8 +63,8 @@ public class DOMResultTest { */ @Test public void testcase01() throws Exception { - String resultFile = USER_DIR + "domresult01.out"; - String goldFile = GOLDEN_DIR + "domresult01GF.out"; + String resultFile = "domresult01.out"; + String goldFile = GOLDEN_DIR + "domresult01GF.out"; String xsltFile = XML_DIR + "cities.xsl"; String xmlFile = XML_DIR + "cities.xml"; @@ -86,7 +85,9 @@ public class DOMResultTest { try (BufferedWriter writer = new BufferedWriter(new FileWriter(resultFile))) { writeNodes(node, writer); } - assertTrue(compareWithGold(goldFile, resultFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(resultFile))); } /** diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/ErrorListenerTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/ErrorListenerTest.java index c591c18fb45..bc067cbcf33 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/ErrorListenerTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/ErrorListenerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -23,19 +23,18 @@ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; - -import java.io.File; +import org.junit.jupiter.api.Test; import javax.xml.transform.ErrorListener; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamSource; +import java.io.File; -import org.testng.annotations.Test; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Class containing the test cases for ErrorListener interface @@ -43,7 +42,7 @@ import org.testng.annotations.Test; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.ErrorListenerTest + * @run junit/othervm javax.xml.transform.ptests.ErrorListenerTest */ public class ErrorListenerTest implements ErrorListener { /** @@ -63,15 +62,13 @@ public class ErrorListenerTest implements ErrorListener { @Test public void errorListener01() { ErrorListenerTest listener = new ErrorListenerTest(); - try { - TransformerFactory tfactory = TransformerFactory.newInstance(); - tfactory.setErrorListener (listener); - tfactory.newTransformer(new StreamSource( - new File(XML_DIR + "invalid.xsl"))); - fail("Expect TransformerConfigurationException here"); - } catch (TransformerConfigurationException ex) { - assertEquals(listener.status, ListenerStatus.FATAL); - } + TransformerFactory tfactory = TransformerFactory.newInstance(); + tfactory.setErrorListener(listener); + StreamSource source = new StreamSource(new File(XML_DIR + "invalid.xsl")); + assertThrows( + TransformerConfigurationException.class, + () -> tfactory.newTransformer(source)); + assertEquals(ListenerStatus.FATAL, listener.status); } /** diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/SAXSourceTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/SAXSourceTest.java index 3e50e8bb79e..851ed676f3a 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/SAXSourceTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/SAXSourceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -23,22 +23,21 @@ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; +import org.junit.jupiter.api.Test; +import org.xml.sax.InputSource; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; -import org.testng.annotations.Test; -import org.xml.sax.InputSource; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; /** @@ -47,7 +46,7 @@ import org.xml.sax.InputSource; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.SAXSourceTest + * @run junit/othervm javax.xml.transform.ptests.SAXSourceTest */ public class SAXSourceTest { /** @@ -57,8 +56,6 @@ public class SAXSourceTest { /** * Test obtaining a SAX InputSource object from a Source object. - * - * @throws IOException reading file error. */ @Test public void source2inputsource01() throws IOException { @@ -72,8 +69,6 @@ public class SAXSourceTest { * This test case tries to get InputSource from DOMSource using * sourceToInputSource method. It is not possible and hence null is * expected. This is a negative test case, - * - * @throws Exception If any errors occur. */ @Test public void source2inputsource02() throws Exception { @@ -87,8 +82,6 @@ public class SAXSourceTest { * This test case tries to get InputSource from SAXSource using * sourceToInputSource method. This will also check if the systemId * remained the same. This is a positive test case. - * - * @throws IOException reading file error. */ @Test public void source2inputsource03() throws IOException { @@ -97,8 +90,7 @@ public class SAXSourceTest { SAXSource saxSource = new SAXSource(new InputSource(fis)); saxSource.setSystemId(SYSTEM_ID); - assertEquals(SAXSource.sourceToInputSource(saxSource).getSystemId(), - SYSTEM_ID); + assertEquals(SYSTEM_ID, SAXSource.sourceToInputSource(saxSource).getSystemId()); } } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/SAXTFactoryTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/SAXTFactoryTest.java index 0e1eabfa8fe..9c12cb9f119 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/SAXTFactoryTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/SAXTFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -23,15 +23,14 @@ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLFilter; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLReaderFactory; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -45,14 +44,17 @@ import javax.xml.transform.sax.TemplatesHandler; import javax.xml.transform.sax.TransformerHandler; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.xml.sax.InputSource; -import org.xml.sax.XMLFilter; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.XMLReaderFactory; +import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Test newTransformerhandler() method which takes StreamSource as argument can @@ -61,7 +63,7 @@ import org.xml.sax.helpers.XMLReaderFactory; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.SAXTFactoryTest + * @run junit/othervm javax.xml.transform.ptests.SAXTFactoryTest */ public class SAXTFactoryTest { /** @@ -84,16 +86,14 @@ public class SAXTFactoryTest { * argument can be set to XMLReader. SAXSource has input XML file as its * input source. XMLReader has a transformer handler which write out the * result to output file. Test verifies output file is same as golden file. - * - * @throws Exception If any errors occur. */ @Test public void testcase01() throws Exception { - String outputFile = USER_DIR + "saxtf001.out"; + String outputFile = "saxtf001.out"; String goldFile = GOLDEN_DIR + "saxtf001GF.out"; try (FileOutputStream fos = new FileOutputStream(outputFile)) { - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); SAXTransformerFactory saxTFactory = (SAXTransformerFactory) TransformerFactory.newInstance(); TransformerHandler handler = saxTFactory.newTransformerHandler(new StreamSource(XSLT_FILE)); @@ -102,7 +102,7 @@ public class SAXTFactoryTest { reader.setContentHandler(handler); reader.parse(XML_FILE); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** @@ -110,17 +110,15 @@ public class SAXTFactoryTest { * argument can be set to XMLReader. SAXSource has input XML file as its * input source. XMLReader has a content handler which write out the result * to output file. Test verifies output file is same as golden file. - * - * @throws Exception If any errors occur. */ @Test public void testcase02() throws Exception { - String outputFile = USER_DIR + "saxtf002.out"; + String outputFile = "saxtf002.out"; String goldFile = GOLDEN_DIR + "saxtf002GF.out"; try (FileOutputStream fos = new FileOutputStream(outputFile); - FileInputStream fis = new FileInputStream(XSLT_FILE)) { - XMLReader reader = XMLReaderFactory.createXMLReader(); + FileInputStream fis = new FileInputStream(XSLT_FILE)) { + XMLReader reader = getXmlReader(); SAXTransformerFactory saxTFactory = (SAXTransformerFactory) TransformerFactory.newInstance(); SAXSource ss = new SAXSource(); @@ -132,18 +130,16 @@ public class SAXTFactoryTest { reader.setContentHandler(handler); reader.parse(XML_FILE); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** * Unit test for newTransformerhandler(Source). DcoumentBuilderFactory is * namespace awareness, DocumentBuilder parse xslt file as DOMSource. - * - * @throws Exception If any errors occur. */ @Test public void testcase03() throws Exception { - String outputFile = USER_DIR + "saxtf003.out"; + String outputFile = "saxtf003.out"; String goldFile = GOLDEN_DIR + "saxtf003GF.out"; try (FileOutputStream fos = new FileOutputStream(outputFile)) { @@ -151,10 +147,9 @@ public class SAXTFactoryTest { dbf.setNamespaceAware(true); DocumentBuilder docBuilder = dbf.newDocumentBuilder(); Document document = docBuilder.parse(new File(XSLT_FILE)); - Node node = (Node)document; - DOMSource domSource= new DOMSource(node); + DOMSource domSource = new DOMSource(document); - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); SAXTransformerFactory saxTFactory = (SAXTransformerFactory)TransformerFactory.newInstance(); TransformerHandler handler = @@ -164,15 +159,13 @@ public class SAXTFactoryTest { reader.setContentHandler(handler); reader.parse(XML_FILE); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** * Negative test for newTransformerHandler when relative URI is in XML file. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = TransformerConfigurationException.class) + @Test public void transformerHandlerTest04() throws Exception { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); @@ -180,31 +173,28 @@ public class SAXTFactoryTest { Document document = docBuilder.parse(new File(XSLT_INCL_FILE)); DOMSource domSource= new DOMSource(document); SAXTransformerFactory saxTFactory - = (SAXTransformerFactory)TransformerFactory.newInstance(); - saxTFactory.newTransformerHandler(domSource); + = (SAXTransformerFactory) TransformerFactory.newInstance(); + assertThrows(TransformerConfigurationException.class, () -> saxTFactory.newTransformerHandler(domSource)); } /** * Unit test for XMLReader parsing when relative URI is used in xsl file and * SystemId was set. - * - * @throws Exception If any errors occur. */ @Test public void testcase05() throws Exception { - String outputFile = USER_DIR + "saxtf005.out"; + String outputFile = "saxtf005.out"; String goldFile = GOLDEN_DIR + "saxtf005GF.out"; try (FileOutputStream fos = new FileOutputStream(outputFile)) { - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); SAXTransformerFactory saxTFactory = (SAXTransformerFactory)TransformerFactory.newInstance(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); DocumentBuilder docBuilder = dbf.newDocumentBuilder(); Document document = docBuilder.parse(new File(XSLT_INCL_FILE)); - Node node = (Node)document; - DOMSource domSource= new DOMSource(node); + DOMSource domSource = new DOMSource(document); domSource.setSystemId("file:///" + XML_DIR); @@ -216,28 +206,26 @@ public class SAXTFactoryTest { reader.setContentHandler(handler); reader.parse(XML_FILE); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** * Unit test newTransformerHandler with a DOMSource. - * - * @throws Exception If any errors occur. */ @Test public void testcase06() throws Exception { - String outputFile = USER_DIR + "saxtf006.out"; + String outputFile = "saxtf006.out"; String goldFile = GOLDEN_DIR + "saxtf006GF.out"; try (FileOutputStream fos = new FileOutputStream(outputFile)) { - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); SAXTransformerFactory saxTFactory = (SAXTransformerFactory)TransformerFactory.newInstance(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); DocumentBuilder docBuilder = dbf.newDocumentBuilder(); - Node node = (Node)docBuilder.parse(new File(XSLT_INCL_FILE)); + Node node = docBuilder.parse(new File(XSLT_INCL_FILE)); DOMSource domSource = new DOMSource(node, "file:///" + XML_DIR); TransformerHandler handler = @@ -248,20 +236,19 @@ public class SAXTFactoryTest { reader.setContentHandler(handler); reader.parse(XML_FILE); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** * Test newTransformerHandler with a Template Handler. - * - * @throws Exception If any errors occur. */ + @Test public void testcase08() throws Exception { - String outputFile = USER_DIR + "saxtf008.out"; + String outputFile = "saxtf008.out"; String goldFile = GOLDEN_DIR + "saxtf008GF.out"; try (FileOutputStream fos = new FileOutputStream(outputFile)) { - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); SAXTransformerFactory saxTFactory = (SAXTransformerFactory)TransformerFactory.newInstance(); @@ -277,22 +264,20 @@ public class SAXTFactoryTest { reader.setContentHandler(tfhandler); reader.parse(XML_FILE); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** * Test newTransformerHandler with a Template Handler along with a relative * URI in the style-sheet file. - * - * @throws Exception If any errors occur. */ @Test public void testcase09() throws Exception { - String outputFile = USER_DIR + "saxtf009.out"; + String outputFile = "saxtf009.out"; String goldFile = GOLDEN_DIR + "saxtf009GF.out"; try (FileOutputStream fos = new FileOutputStream(outputFile)) { - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); SAXTransformerFactory saxTFactory = (SAXTransformerFactory)TransformerFactory.newInstance(); @@ -307,21 +292,19 @@ public class SAXTFactoryTest { reader.setContentHandler(tfhandler); reader.parse(XML_FILE); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** * Unit test for contentHandler setter/getter along reader as handler's * parent. - * - * @throws Exception If any errors occur. */ @Test public void testcase10() throws Exception { - String outputFile = USER_DIR + "saxtf010.out"; + String outputFile = "saxtf010.out"; String goldFile = GOLDEN_DIR + "saxtf010GF.out"; // The transformer will use a SAX parser as it's reader. - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); SAXTransformerFactory saxTFactory = (SAXTransformerFactory)TransformerFactory.newInstance(); XMLFilter filter = @@ -333,26 +316,23 @@ public class SAXTFactoryTest { // the content handler for the parser object (it's "parent"), and // will then call the parse method on the parser. filter.parse(new InputSource(XML_FILE)); - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** * Unit test for contentHandler setter/getter with parent. - * - * @throws Exception If any errors occur. */ @Test public void testcase11() throws Exception { - String outputFile = USER_DIR + "saxtf011.out"; + String outputFile = "saxtf011.out"; String goldFile = GOLDEN_DIR + "saxtf011GF.out"; // The transformer will use a SAX parser as it's reader. - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); DocumentBuilder docBuilder = dbf.newDocumentBuilder(); Document document = docBuilder.parse(new File(XSLT_FILE)); - Node node = (Node)document; - DOMSource domSource= new DOMSource(node); + DOMSource domSource = new DOMSource(document); SAXTransformerFactory saxTFactory = (SAXTransformerFactory)TransformerFactory.newInstance(); @@ -365,20 +345,18 @@ public class SAXTFactoryTest { // the content handler for the parser object (it's "parent"), and // will then call the parse method on the parser. filter.parse(new InputSource(XML_FILE)); - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** * Unit test for contentHandler setter/getter. - * - * @throws Exception If any errors occur. */ @Test public void testcase12() throws Exception { - String outputFile = USER_DIR + "saxtf012.out"; + String outputFile = "saxtf012.out"; String goldFile = GOLDEN_DIR + "saxtf012GF.out"; // The transformer will use a SAX parser as it's reader. - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); InputSource is = new InputSource(new FileInputStream(XSLT_FILE)); SAXSource saxSource = new SAXSource(); @@ -394,27 +372,26 @@ public class SAXTFactoryTest { // the content handler for the parser object (it's "parent"), and // will then call the parse method on the parser. filter.parse(new InputSource(XML_FILE)); - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** * Unit test for TemplatesHandler setter/getter. - * - * @throws Exception If any errors occur. */ @Test public void testcase13() throws Exception { - String outputFile = USER_DIR + "saxtf013.out"; + String outputFile = "saxtf013.out"; String goldFile = GOLDEN_DIR + "saxtf013GF.out"; try(FileInputStream fis = new FileInputStream(XML_FILE)) { // The transformer will use a SAX parser as it's reader. - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); SAXTransformerFactory saxTFactory = (SAXTransformerFactory) TransformerFactory.newInstance(); TemplatesHandler thandler = saxTFactory.newTemplatesHandler(); - // I have put this as it was complaining about systemid - thandler.setSystemId("file:///" + USER_DIR); + // Set the root directory as the ID so the handler can resolve relative paths. + String rootDirUri = Path.of(".").toAbsolutePath().toUri().toASCIIString(); + thandler.setSystemId(rootDirUri); reader.setContentHandler(thandler); reader.parse(XSLT_FILE); @@ -425,6 +402,17 @@ public class SAXTFactoryTest { filter.setContentHandler(new MyContentHandler(outputFile)); filter.parse(new InputSource(fis)); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); + } + + @SuppressWarnings("deprecation") + private static XMLReader getXmlReader() throws SAXException { + return XMLReaderFactory.createXMLReader(); + } + + private static void assertFileLines(String goldenFile, String actualFile) throws IOException { + assertLinesMatch( + Files.readAllLines(Path.of(goldenFile)), + Files.readAllLines(Path.of(actualFile))); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/StreamResultTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/StreamResultTest.java index 6002e371523..abd15888493 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/StreamResultTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/StreamResultTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,15 +22,9 @@ */ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.failUnexpected; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.Arrays; -import java.util.Properties; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -41,10 +35,14 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Properties; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.xml.sax.SAXException; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.fail; /** * Test a StreamResult using a file name that contains URL characters that need @@ -53,7 +51,7 @@ import org.xml.sax.SAXException; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.StreamResultTest + * @run junit/othervm javax.xml.transform.ptests.StreamResultTest */ public class StreamResultTest { /** @@ -90,15 +88,15 @@ public class StreamResultTest { DOMSource domSource = new DOMSource(document); StreamSource streamSource = new StreamSource(new FileInputStream(xmlFile)); - File streamResultFile = new File(USER_DIR + file); + File streamResultFile = new File(file); StreamResult streamResult = new StreamResult(streamResultFile); Transformer transformer = TransformerFactory.newInstance().newTransformer(domSource); transformer.setOutputProperties(transformProperties); transformer.transform(streamSource, streamResult); } catch (SAXException | IOException | ParserConfigurationException - | TransformerException ex) { - failUnexpected(ex); + | TransformerException ex) { + fail(ex); } }); } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TfClearParamTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TfClearParamTest.java index f647d4c0bf1..01dcf3e6b97 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TfClearParamTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TfClearParamTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,12 +22,10 @@ */ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; - -import java.io.File; -import java.io.FileInputStream; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.xml.sax.InputSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -37,11 +35,12 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.io.FileInputStream; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.xml.sax.InputSource; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; /** * Class containing the test cases for SAXParserFactory API @@ -49,7 +48,7 @@ import org.xml.sax.InputSource; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.TfClearParamTest + * @run junit/othervm javax.xml.transform.ptests.TfClearParamTest */ public class TfClearParamTest { /** @@ -82,7 +81,7 @@ public class TfClearParamTest { public void clear01() throws TransformerConfigurationException { Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setParameter(LONG_PARAM_NAME, PARAM_VALUE); - assertEquals(transformer.getParameter(LONG_PARAM_NAME).toString(), PARAM_VALUE); + assertEquals(PARAM_VALUE, transformer.getParameter(LONG_PARAM_NAME).toString()); } /** @@ -124,9 +123,9 @@ public class TfClearParamTest { public void clear04() throws TransformerConfigurationException { Transformer transformer = TransformerFactory.newInstance().newTransformer(); - int intObject = 5; - transformer.setParameter(SHORT_PARAM_NAME, intObject); - assertEquals(transformer.getParameter(SHORT_PARAM_NAME), intObject); + int expectedIntValue = 5; + transformer.setParameter(SHORT_PARAM_NAME, expectedIntValue); + assertEquals(expectedIntValue, transformer.getParameter(SHORT_PARAM_NAME)); } /** @@ -141,7 +140,7 @@ public class TfClearParamTest { newTransformer(new StreamSource(new File(XSL_FILE))); transformer.setParameter(LONG_PARAM_NAME, PARAM_VALUE); - assertEquals(transformer.getParameter(LONG_PARAM_NAME), PARAM_VALUE); + assertEquals(PARAM_VALUE, transformer.getParameter(LONG_PARAM_NAME)); } /** @@ -172,7 +171,7 @@ public class TfClearParamTest { Transformer transformer = TransformerFactory.newInstance().newTransformer(saxSource); transformer.setParameter(LONG_PARAM_NAME, PARAM_VALUE); - assertEquals(transformer.getParameter(LONG_PARAM_NAME), PARAM_VALUE); + assertEquals(PARAM_VALUE, transformer.getParameter(LONG_PARAM_NAME)); } } @@ -212,7 +211,7 @@ public class TfClearParamTest { Transformer transformer = tfactory.newTransformer(domSource); transformer.setParameter(LONG_PARAM_NAME, PARAM_VALUE); - assertEquals(transformer.getParameter(LONG_PARAM_NAME), PARAM_VALUE); + assertEquals(PARAM_VALUE, transformer.getParameter(LONG_PARAM_NAME)); } /** diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformTest.java index 8666306669d..1d9600b330b 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -23,18 +23,19 @@ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; - -import java.io.BufferedWriter; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.function.Supplier; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -56,30 +57,33 @@ import javax.xml.transform.stax.StAXResult; import javax.xml.transform.stax.StAXSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.function.Supplier; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.xml.sax.Attributes; -import org.xml.sax.ContentHandler; -import org.xml.sax.InputSource; -import org.xml.sax.Locator; -import org.xml.sax.SAXException; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.TransformTest + * @run junit/othervm javax.xml.transform.ptests.TransformTest * @summary Tests for variable combination of Transformer.transform(Source, Result) */ -@Test(singleThreaded = true) +@TestInstance(Lifecycle.PER_CLASS) +@Execution(ExecutionMode.SAME_THREAD) public class TransformTest { /* * Initialize the share objects. */ - @BeforeClass + @BeforeAll public void setup() throws Exception { ifac = XMLInputFactory.newInstance(); ofac = XMLOutputFactory.newInstance(); @@ -95,7 +99,6 @@ public class TransformTest { xmlDoc = db.parse(xmlInputStream()); } - @DataProvider(name = "input-provider") public Object[][] prepareTestCombination() throws Exception { Supplier staxStreamSource = () -> new StAXSource(getXMLStreamReader()); @@ -160,7 +163,8 @@ public class TransformTest { /* * run Transformer.transform(Source, Result) */ - @Test(dataProvider = "input-provider") + @ParameterizedTest + @MethodSource("prepareTestCombination") public void testTransform(Supplier src, Supplier res, Transformer transformer) throws Throwable { try { transformer.transform(src.get(), res.get()); @@ -257,7 +261,7 @@ public class TransformTest { public void setDocumentLocator(Locator locator) { } - public void startDocument() throws SAXException { + public void startDocument() { String str = "startDocument"; try { bWriter.write(str, 0, str.length()); @@ -267,7 +271,7 @@ public class TransformTest { } } - public void endDocument() throws SAXException { + public void endDocument() { String str = "endDocument"; try { bWriter.write(str, 0, str.length()); @@ -279,7 +283,7 @@ public class TransformTest { } } - public void startPrefixMapping(String prefix, String uri) throws SAXException { + public void startPrefixMapping(String prefix, String uri) { String str = "startPrefixMapping: " + prefix + ", " + uri; try { bWriter.write(str, 0, str.length()); @@ -289,7 +293,7 @@ public class TransformTest { } } - public void endPrefixMapping(String prefix) throws SAXException { + public void endPrefixMapping(String prefix) { String str = "endPrefixMapping: " + prefix; try { bWriter.write(str, 0, str.length()); @@ -299,7 +303,7 @@ public class TransformTest { } } - public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { + public void startElement(String namespaceURI, String localName, String qName, Attributes atts) { StringBuilder str = new StringBuilder("startElement: ").append(namespaceURI).append(", ").append(namespaceURI).append(", ").append(qName).append(" : "); int n = atts.getLength(); for (int i = 0; i < n; i++) { @@ -314,7 +318,7 @@ public class TransformTest { } } - public void endElement(String namespaceURI, String localName, String qName) throws SAXException { + public void endElement(String namespaceURI, String localName, String qName) { String str = "endElement: " + namespaceURI + ", " + namespaceURI + ", " + qName; try { bWriter.write(str, 0, str.length()); @@ -325,7 +329,7 @@ public class TransformTest { } - public void characters(char ch[], int start, int length) throws SAXException { + public void characters(char ch[], int start, int length) { String str = new String(ch, start, length); try { bWriter.write(str, 0, str.length()); @@ -335,7 +339,7 @@ public class TransformTest { } } - public void ignorableWhitespace(char ch[], int start, int length) throws SAXException { + public void ignorableWhitespace(char ch[], int start, int length) { String str = "ignorableWhitespace"; try { bWriter.write(str, 0, str.length()); @@ -345,7 +349,7 @@ public class TransformTest { } } - public void processingInstruction(String target, String data) throws SAXException { + public void processingInstruction(String target, String data) { String str = "processingInstruction: " + target + ", " + target; try { bWriter.write(str, 0, str.length()); @@ -355,7 +359,7 @@ public class TransformTest { } } - public void skippedEntity(String name) throws SAXException { + public void skippedEntity(String name) { String str = "skippedEntity: " + name; try { bWriter.write(str, 0, str.length()); diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerExcpTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerExcpTest.java index 8907b7afd55..25b715864dd 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerExcpTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerExcpTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,21 +22,21 @@ */ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.fail; - -import java.io.File; +import org.junit.jupiter.api.Test; import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.sax.SAXResult; import javax.xml.transform.stream.StreamSource; +import java.io.File; -import org.testng.annotations.Test; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Basic test for TransformerException specification. @@ -44,31 +44,28 @@ import org.testng.annotations.Test; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.TransformerExcpTest + * @run junit/othervm javax.xml.transform.ptests.TransformerExcpTest */ public class TransformerExcpTest { /** * Transform an unformatted style-sheet file. TransformerException is thrown. */ @Test - public void tfexception() { - try { - // invalid.xsl has well-formedness error. Therefore transform throws - // TransformerException - StreamSource streamSource - = new StreamSource(new File(XML_DIR + "invalid.xsl")); - TransformerFactory tFactory = TransformerFactory.newInstance(); - Transformer transformer = tFactory.newTransformer(streamSource); - transformer.transform( - new StreamSource(new File(XML_DIR + "cities.xml")), - new SAXResult()); - fail("TransformerException is not thrown as expected"); - } catch (TransformerException e) { - assertNotNull(e.getCause()); - assertNotNull(e.getException()); - assertNull(e.getLocationAsString()); - assertEquals(e.getMessageAndLocation(),e.getMessage()); - } + public void tfexception() throws TransformerConfigurationException { + // invalid.xsl has well-formedness error. Therefore transform throws + // TransformerException + StreamSource streamSource + = new StreamSource(new File(XML_DIR + "invalid.xsl")); + TransformerFactory tFactory = TransformerFactory.newInstance(); + + TransformerException e = assertThrows( + TransformerException.class, + () -> tFactory.newTransformer(streamSource)); + + assertNotNull(e.getCause()); + assertNotNull(e.getException()); + assertNull(e.getLocationAsString()); + assertEquals(e.getMessageAndLocation(), e.getMessage()); } @@ -77,20 +74,20 @@ public class TransformerExcpTest { * TransformerException(Throwable), initCause should throw * IllegalStateException */ - @Test(expectedExceptions = IllegalStateException.class) + @Test public void tfexception06() { TransformerException te = new TransformerException(new Throwable()); - te.initCause(null); + assertThrows(IllegalStateException.class, () -> te.initCause(null)); } /** * Spec says, "if the throwable was created with TransformerException(String, * Throwable), initCause should throw IllegalStateException */ - @Test(expectedExceptions = IllegalStateException.class) + @Test public void tfexception07() { TransformerException te = new TransformerException("MyMessage", new Throwable()); - te.initCause(null); + assertThrows(IllegalStateException.class, () -> te.initCause(null)); } /** diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerFactoryTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerFactoryTest.java index 8223319aa0a..b53fe298005 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerFactoryTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,18 +22,11 @@ */ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertNotSame; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertEquals; - -import java.io.FileInputStream; -import java.io.FileOutputStream; +import jaxp.library.JAXPDataProvider; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -44,12 +37,19 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.nio.file.Files; +import java.nio.file.Path; -import jaxp.library.JAXPDataProvider; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.w3c.dom.Document; +import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Class containing the test cases for TransformerFactory API's @@ -59,7 +59,8 @@ import org.w3c.dom.Document; * @test * @bug 8169778 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.TransformerFactoryTest + * @build jaxp.library.JAXPDataProvider + * @run junit/othervm javax.xml.transform.ptests.TransformerFactoryTest */ public class TransformerFactoryTest { /** @@ -78,24 +79,25 @@ public class TransformerFactoryTest { * * @return a data provider contains TransformerFactory instantiation parameters. */ - @DataProvider(name = "parameters") - public Object[][] getValidateParameters() { - return new Object[][] { { TRANSFORMER_FACTORY_CLASSNAME, null }, { TRANSFORMER_FACTORY_CLASSNAME, this.getClass().getClassLoader() } }; + public static Object[][] getValidateParameters() { + return new Object[][] { + { TRANSFORMER_FACTORY_CLASSNAME, null }, + { TRANSFORMER_FACTORY_CLASSNAME, TransformerFactoryTest.class.getClassLoader() }, + }; } /** * Test if newDefaultInstance() method returns an instance * of the expected factory. - * @throws Exception If any errors occur. */ @Test - public void testDefaultInstance() throws Exception { + public void testDefaultInstance() { TransformerFactory tf1 = TransformerFactory.newDefaultInstance(); TransformerFactory tf2 = TransformerFactory.newInstance(); assertNotSame(tf1, tf2, "same instance returned:"); assertSame(tf1.getClass(), tf2.getClass(), - "unexpected class mismatch for newDefaultInstance():"); - assertEquals(tf1.getClass().getName(), DEFAULT_IMPL_CLASS); + "unexpected class mismatch for newDefaultInstance():"); + assertEquals(DEFAULT_IMPL_CLASS, tf1.getClass().getName()); } /** @@ -104,12 +106,9 @@ public class TransformerFactoryTest { * points to correct implementation of * javax.xml.transform.TransformerFactory , should return newInstance of * TransformerFactory - * - * @param factoryClassName - * @param classLoader - * @throws TransformerConfigurationException */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("getValidateParameters") public void testNewInstance(String factoryClassName, ClassLoader classLoader) throws TransformerConfigurationException { TransformerFactory tf = TransformerFactory.newInstance(factoryClassName, classLoader); Transformer transformer = tf.newTransformer(); @@ -120,13 +119,13 @@ public class TransformerFactoryTest { * Test for TransformerFactory.newInstance(java.lang.String * factoryClassName, java.lang.ClassLoader classLoader) factoryClassName is * null , should throw TransformerFactoryConfigurationError - * - * @param factoryClassName - * @param classLoader */ - @Test(expectedExceptions = TransformerFactoryConfigurationError.class, dataProvider = "new-instance-neg", dataProviderClass = JAXPDataProvider.class) + @ParameterizedTest + @MethodSource("jaxp.library.JAXPDataProvider#newInstanceNeg") public void testNewInstanceNeg(String factoryClassName, ClassLoader classLoader) { - TransformerFactory.newInstance(factoryClassName, classLoader); + assertThrows( + TransformerFactoryConfigurationError.class, + () -> TransformerFactory.newInstance(factoryClassName, classLoader)); } /** @@ -134,12 +133,10 @@ public class TransformerFactoryTest { * of TransformerFactory. * The style sheet returned is then copied to an tfactory01.out * It will then be verified to see if it matches the golden files. - * - * @throws Exception If any errors occur. */ @Test public void tfactory01() throws Exception { - String outputFile = USER_DIR + "tfactory01.out"; + String outputFile = "tfactory01.out"; String goldFile = GOLDEN_DIR + "tfactory01GF.out"; String xmlFile = XML_DIR + "TransformerFactoryTest.xml"; String xmlURI = "file:///" + XML_DIR; @@ -160,6 +157,8 @@ public class TransformerFactoryTest { Transformer t = tFactory.newTransformer(); t.transform(s, streamResult); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest.java index f940f268ff4..6a779cc05e0 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,14 +22,9 @@ */ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; - -import java.io.File; -import java.io.FileInputStream; -import java.util.Properties; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -41,10 +36,14 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.io.FileInputStream; +import java.util.Properties; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.xml.sax.InputSource; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * Basic test cases for Transformer API @@ -52,7 +51,7 @@ import org.xml.sax.InputSource; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.TransformerTest + * @run junit/othervm javax.xml.transform.ptests.TransformerTest */ public class TransformerTest { /** @@ -90,8 +89,6 @@ public class TransformerTest { /** * This tests if newTransformer(DOMSource) method returns Transformer. - * - * @throws Exception If any errors occur. */ @Test public void transformer03() throws Exception { @@ -109,8 +106,6 @@ public class TransformerTest { /** * This tests set/get ErrorListener methods of Transformer. - * - * @throws Exception If any errors occur. */ @Test public void transformer04() throws Exception { @@ -124,13 +119,11 @@ public class TransformerTest { .newTransformer(domSource); transformer.setErrorListener(new MyErrorListener()); assertNotNull(transformer.getErrorListener()); - assertTrue(transformer.getErrorListener() instanceof MyErrorListener); + assertInstanceOf(MyErrorListener.class, transformer.getErrorListener()); } /** * This tests getOutputProperties() method of Transformer. - * - * @throws Exception If any errors occur. */ @Test public void transformer05() throws Exception { @@ -144,18 +137,16 @@ public class TransformerTest { newTransformer(domSource); Properties prop = transformer.getOutputProperties(); - assertEquals(prop.getProperty("indent"), "yes"); - assertEquals(prop.getProperty("method"), "xml"); - assertEquals(prop.getProperty("encoding"), "UTF-8"); - assertEquals(prop.getProperty("standalone"), "no"); - assertEquals(prop.getProperty("version"), "1.0"); - assertEquals(prop.getProperty("omit-xml-declaration"), "no"); + assertEquals("yes", prop.getProperty("indent")); + assertEquals("xml", prop.getProperty("method")); + assertEquals("UTF-8", prop.getProperty("encoding")); + assertEquals("no", prop.getProperty("standalone")); + assertEquals("1.0", prop.getProperty("version")); + assertEquals("no", prop.getProperty("omit-xml-declaration")); } /** * This tests getOutputProperty() method of Transformer. - * - * @throws Exception If any errors occur. */ @Test public void transformer06() throws Exception { @@ -168,7 +159,7 @@ public class TransformerTest { DOMSource domSource = new DOMSource(document); Transformer transformer = tfactory.newTransformer(domSource); - assertEquals(transformer.getOutputProperty("method"), "xml"); + assertEquals("xml", transformer.getOutputProperty("method")); } } @@ -199,8 +190,7 @@ class MyErrorListener implements ErrorListener { * @param e exception of a fatal error. */ @Override - public void fatalError (TransformerException e) throws - TransformerException { + public void fatalError(TransformerException e) { System.out.println(" In fatal"); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest02.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest02.java index a9391b02866..0b7a9a85f09 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest02.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest02.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,15 +22,7 @@ */ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; +import org.junit.jupiter.api.Test; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Transformer; @@ -38,8 +30,15 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.nio.file.Files; +import java.nio.file.Path; -import org.testng.annotations.Test; +import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; /** @@ -49,17 +48,15 @@ import org.testng.annotations.Test; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.TransformerTest02 + * @run junit/othervm javax.xml.transform.ptests.TransformerTest02 */ public class TransformerTest02 { /** * Unit test for transform(StreamSource, StreamResult). - * - * @throws Exception If any errors occur. */ @Test public void testcase01() throws Exception { - String outputFile = USER_DIR + "transformer02.out"; + String outputFile = "transformer02.out"; String goldFile = GOLDEN_DIR + "transformer02GF.out"; String xsltFile = XML_DIR + "cities.xsl"; String xmlFile = XML_DIR + "cities.xml"; @@ -79,6 +76,8 @@ public class TransformerTest02 { transformer.setOutputProperty("indent", "no"); transformer.transform(streamSource, streamResult); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest03.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest03.java index 4cdd2e606f1..18e5033b0f0 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest03.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest03.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,16 +22,7 @@ */ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.util.Properties; +import org.junit.jupiter.api.Test; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Transformer; @@ -39,8 +30,16 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Properties; -import org.testng.annotations.Test; +import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; /** * Here Properties Object is populated with required properties.A transformer @@ -51,17 +50,15 @@ import org.testng.annotations.Test; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.TransformerTest03 + * @run junit/othervm javax.xml.transform.ptests.TransformerTest03 */ public class TransformerTest03 { /** * Test for Transformer.setOutputProperties method. - * - * @throws Exception If any errors occur. */ @Test public void testcase01() throws Exception { - String outputFile = USER_DIR + "transformer03.out"; + String outputFile = "transformer03.out"; String goldFile = GOLDEN_DIR + "transformer03GF.out"; String xsltFile = XML_DIR + "cities.xsl"; String xmlFile = XML_DIR + "cities.xml"; @@ -88,6 +85,8 @@ public class TransformerTest03 { transformer.setOutputProperties(properties); transformer.transform(new StreamSource(fis), new StreamResult(fos)); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/URIResolverTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/URIResolverTest.java index 0d642b67129..3db6bec77ae 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/URIResolverTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/URIResolverTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,12 +22,9 @@ */ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; - -import java.io.File; -import java.io.FileInputStream; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -39,10 +36,12 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.io.FileInputStream; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.xml.sax.InputSource; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * URIResolver should be invoked when transform happens. @@ -50,9 +49,9 @@ import org.xml.sax.InputSource; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.URIResolverTest + * @run junit/othervm javax.xml.transform.ptests.URIResolverTest */ -public class URIResolverTest implements URIResolver { +public class URIResolverTest { /** * System ID constant. */ @@ -73,60 +72,29 @@ public class URIResolverTest implements URIResolver { */ private final static String XSL_TEMP_FILE = "temp/cities.xsl"; - /** - * expected HREF. - */ - private final String validateHref; - - /** - * expected Base URI. - */ - private final String validateBase; - - /** - * Default constructor for testng invocation. - */ - public URIResolverTest(){ - validateHref = null; - validateBase = null; - } - - /** - * Constructor for setting expected Href and expected Base URI. - * @param validateHref expected Href - * @param validateBase expected Base URI - */ - public URIResolverTest(String validateHref, String validateBase){ - this.validateHref = validateHref; - this.validateBase = validateBase; - } - - /** - * Called by the processor when it encounters an xsl:include, xsl:import, - * or document() function. - * @param href An href attribute, which may be relative or absolute. - * @param base The base URI against which the first argument will be made - * absolute if the absolute URI is required. - * @return null always. - */ - @Override - public Source resolve(String href, String base) { - assertEquals(href, validateHref); - assertEquals(base, validateBase); - return null; + record TestResolver(String expectedHref, String expectedBase) implements URIResolver { + /** + * Called by the processor when it encounters an xsl:include, xsl:import, + * or document() function. + */ + @Override + public Source resolve(String href, String base) { + assertEquals(expectedHref, href); + assertEquals(expectedBase, base); + // Return null if the href cannot be resolved. + return null; + } } /** * This is to test the URIResolver.resolve() method when a transformer is * created using StreamSource. style-sheet file has xsl:include in it. - * - * @throws Exception If any errors occur. */ @Test - public static void resolver01() throws Exception { + public void resolver01() throws Exception { try (FileInputStream fis = new FileInputStream(XSL_INCLUDE_FILE)) { TransformerFactory tfactory = TransformerFactory.newInstance(); - URIResolverTest resolver = new URIResolverTest(XSL_TEMP_FILE, SYSTEM_ID); + TestResolver resolver = new TestResolver(XSL_TEMP_FILE, SYSTEM_ID); tfactory.setURIResolver(resolver); StreamSource streamSource = new StreamSource(fis); @@ -138,13 +106,11 @@ public class URIResolverTest implements URIResolver { /** * This is to test the URIResolver.resolve() method when a transformer is * created using DOMSource. style-sheet file has xsl:include in it. - * - * @throws Exception If any errors occur. */ @Test - public static void resolver02() throws Exception { + public void resolver02() throws Exception { TransformerFactory tfactory = TransformerFactory.newInstance(); - URIResolverTest resolver = new URIResolverTest(XSL_TEMP_FILE, SYSTEM_ID); + TestResolver resolver = new TestResolver(XSL_TEMP_FILE, SYSTEM_ID); tfactory.setURIResolver(resolver); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); @@ -159,13 +125,11 @@ public class URIResolverTest implements URIResolver { /** * This is to test the URIResolver.resolve() method when a transformer is * created using SAXSource. style-sheet file has xsl:include in it. - * - * @throws Exception If any errors occur. */ @Test - public static void resolver03() throws Exception { - try (FileInputStream fis = new FileInputStream(XSL_INCLUDE_FILE)){ - URIResolverTest resolver = new URIResolverTest(XSL_TEMP_FILE, SYSTEM_ID); + public void resolver03() throws Exception { + try (FileInputStream fis = new FileInputStream(XSL_INCLUDE_FILE)) { + TestResolver resolver = new TestResolver(XSL_TEMP_FILE, SYSTEM_ID); TransformerFactory tfactory = TransformerFactory.newInstance(); tfactory.setURIResolver(resolver); InputSource is = new InputSource(fis); @@ -178,13 +142,11 @@ public class URIResolverTest implements URIResolver { /** * This is to test the URIResolver.resolve() method when a transformer is * created using StreamSource. style-sheet file has xsl:import in it. - * - * @throws Exception If any errors occur. */ @Test - public static void resolver04() throws Exception { + public void resolver04() throws Exception { try (FileInputStream fis = new FileInputStream(XSL_IMPORT_FILE)) { - URIResolverTest resolver = new URIResolverTest(XSL_TEMP_FILE, SYSTEM_ID); + TestResolver resolver = new TestResolver(XSL_TEMP_FILE, SYSTEM_ID); TransformerFactory tfactory = TransformerFactory.newInstance(); tfactory.setURIResolver(resolver); StreamSource streamSource = new StreamSource(fis); @@ -196,12 +158,10 @@ public class URIResolverTest implements URIResolver { /** * This is to test the URIResolver.resolve() method when a transformer is * created using DOMSource. style-sheet file has xsl:import in it. - * - * @throws Exception If any errors occur. */ @Test - public static void resolver05() throws Exception { - URIResolverTest resolver = new URIResolverTest(XSL_TEMP_FILE, SYSTEM_ID); + public void resolver05() throws Exception { + TestResolver resolver = new TestResolver(XSL_TEMP_FILE, SYSTEM_ID); TransformerFactory tfactory = TransformerFactory.newInstance(); tfactory.setURIResolver(resolver); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); @@ -215,13 +175,11 @@ public class URIResolverTest implements URIResolver { /** * This is to test the URIResolver.resolve() method when a transformer is * created using SAXSource. style-sheet file has xsl:import in it. - * - * @throws Exception If any errors occur. */ @Test - public static void resolver06() throws Exception { - try (FileInputStream fis = new FileInputStream(XSL_IMPORT_FILE)){ - URIResolverTest resolver = new URIResolverTest(XSL_TEMP_FILE, SYSTEM_ID); + public void resolver06() throws Exception { + try (FileInputStream fis = new FileInputStream(XSL_IMPORT_FILE)) { + TestResolver resolver = new TestResolver(XSL_TEMP_FILE, SYSTEM_ID); TransformerFactory tfactory = TransformerFactory.newInstance(); tfactory.setURIResolver(resolver); InputSource is = new InputSource(fis); @@ -234,13 +192,11 @@ public class URIResolverTest implements URIResolver { /** * This is to test the URIResolver.resolve() method when there is an error * in the file. - * - * @throws Exception If any errors occur. */ @Test - public static void docResolver01() throws Exception { + public void docResolver01() throws Exception { try (FileInputStream fis = new FileInputStream(XML_DIR + "doctest.xsl")) { - URIResolverTest resolver = new URIResolverTest("temp/colors.xml", SYSTEM_ID); + TestResolver resolver = new TestResolver("temp/colors.xml", SYSTEM_ID); StreamSource streamSource = new StreamSource(fis); streamSource.setSystemId(SYSTEM_ID); diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/othervm/TFCErrorTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/othervm/TFCErrorTest.java index 41a528ce1d2..31fa2960250 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/othervm/TFCErrorTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/othervm/TFCErrorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -22,14 +22,13 @@ */ package javax.xml.transform.ptests.othervm; -import static jaxp.library.JAXPTestUtilities.setSystemProperty; - -import static org.testng.Assert.fail; +import org.junit.jupiter.api.Test; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Negative test for set invalid TransformerFactory property. @@ -37,17 +36,21 @@ import org.testng.annotations.Test; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.othervm.TFCErrorTest + * @run junit/othervm javax.xml.transform.ptests.othervm.TFCErrorTest */ public class TFCErrorTest { - @Test(expectedExceptions = ClassNotFoundException.class) - public void tfce01() throws Exception { - try{ - setSystemProperty("javax.xml.transform.TransformerFactory","xx"); - TransformerFactory.newInstance(); - fail("Expect TransformerFactoryConfigurationError here"); - } catch (TransformerFactoryConfigurationError expected) { - throw expected.getException(); + private static final String TRANSFORMER_FACTORY = "javax.xml.transform.TransformerFactory"; + + @Test + public void tfce01() { + System.setProperty(TRANSFORMER_FACTORY, "xx"); + try { + TransformerFactoryConfigurationError e = assertThrows( + TransformerFactoryConfigurationError.class, + TransformerFactory::newInstance); + assertInstanceOf(ClassNotFoundException.class, e.getException()); + } finally { + System.clearProperty(TRANSFORMER_FACTORY); } } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/xmlfiles/out/saxtf008GF.out b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/xmlfiles/out/saxtf008GF.out index 1fc9136a82b..4e646319c6e 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/xmlfiles/out/saxtf008GF.out +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/xmlfiles/out/saxtf008GF.out @@ -1,17 +1,17 @@ - -Paris -Nice -Lyon - - -Roma -Milano -Firenze -Napoli - - -Madrid -Barcelona - + + Paris + Nice + Lyon + + + Roma + Milano + Firenze + Napoli + + + Madrid + Barcelona + diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/SchemaFactoryTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/SchemaFactoryTest.java index d9721883870..dc5acdfe1a3 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/SchemaFactoryTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/SchemaFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -22,23 +22,22 @@ */ package javax.xml.validation.ptests; -import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotSame; -import static org.testng.Assert.assertEquals; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Paths; +import jaxp.library.JAXPDataProvider; +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 org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.SAXParseException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -52,31 +51,38 @@ import javax.xml.transform.stax.StAXSource; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; -import jaxp.library.JAXPDataProvider; - -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.xml.sax.ErrorHandler; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.SAXNotRecognizedException; -import org.xml.sax.SAXNotSupportedException; -import org.xml.sax.SAXParseException; +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @bug 8080907 8169778 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.validation.ptests.SchemaFactoryTest + * @build jaxp.library.JAXPDataProvider + * @run junit/othervm javax.xml.validation.ptests.SchemaFactoryTest * @summary Class containing the test cases for SchemaFactory */ -@Test(singleThreaded = true) +@Execution(ExecutionMode.SAME_THREAD) +@TestInstance(Lifecycle.PER_CLASS) public class SchemaFactoryTest { - @BeforeClass + @BeforeAll public void setup() throws SAXException, IOException, ParserConfigurationException { sf = newSchemaFactory(); assertNotNull(sf); @@ -96,7 +102,6 @@ public class SchemaFactoryTest { } - @DataProvider(name = "parameters") public Object[][] getValidateParameters() { return new Object[][] { { W3C_XML_SCHEMA_NS_URI, SCHEMA_FACTORY_CLASSNAME, null }, { W3C_XML_SCHEMA_NS_URI, SCHEMA_FACTORY_CLASSNAME, this.getClass().getClassLoader() } }; @@ -105,16 +110,15 @@ public class SchemaFactoryTest { /** * Test if newDefaultInstance() method returns an instance * of the expected factory. - * @throws Exception If any errors occur. */ @Test - public void testDefaultInstance() throws Exception { + public void testDefaultInstance() { SchemaFactory sf1 = SchemaFactory.newDefaultInstance(); SchemaFactory sf2 = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI); assertNotSame(sf1, sf2, "same instance returned:"); assertSame(sf1.getClass(), sf2.getClass(), - "unexpected class mismatch for newDefaultInstance():"); - assertEquals(sf1.getClass().getName(), DEFAULT_IMPL_CLASS); + "unexpected class mismatch for newDefaultInstance():"); + assertEquals(DEFAULT_IMPL_CLASS, sf1.getClass().getName()); assertTrue(sf1.isSchemaLanguageSupported(W3C_XML_SCHEMA_NS_URI), "isSchemaLanguageSupported(W3C_XML_SCHEMA_NS_URI):"); assertFalse(sf1.isSchemaLanguageSupported(UNRECOGNIZED_NAME), @@ -128,9 +132,10 @@ public class SchemaFactoryTest { * javax.xml.validation.SchemaFactory , should return newInstance of * SchemaFactory */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("getValidateParameters") public void testNewInstance(String schemaLanguage, String factoryClassName, ClassLoader classLoader) throws SAXException { - SchemaFactory sf = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI, SCHEMA_FACTORY_CLASSNAME, null); + SchemaFactory sf = SchemaFactory.newInstance(schemaLanguage, factoryClassName, classLoader); Schema schema = sf.newSchema(); assertNotNull(schema); } @@ -140,10 +145,12 @@ public class SchemaFactoryTest { * java.lang.String factoryClassName, java.lang.ClassLoader classLoader) * factoryClassName is null , should throw IllegalArgumentException */ - @Test(expectedExceptions = IllegalArgumentException.class, dataProvider = "new-instance-neg", dataProviderClass = JAXPDataProvider.class) + @ParameterizedTest + @MethodSource("jaxp.library.JAXPDataProvider#newInstanceNeg") public void testNewInstanceWithNullFactoryClassName(String factoryClassName, ClassLoader classLoader) { - - SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI, factoryClassName, classLoader); + assertThrows( + IllegalArgumentException.class, + () -> SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI, factoryClassName, classLoader)); } /* @@ -151,9 +158,11 @@ public class SchemaFactoryTest { * java.lang.String factoryClassName, java.lang.ClassLoader classLoader) * schemaLanguage is null , should throw NPE */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testNewInstanceWithNullSchemaLanguage() { - SchemaFactory.newInstance(null, SCHEMA_FACTORY_CLASSNAME, this.getClass().getClassLoader()); + assertThrows( + NullPointerException.class, + () -> SchemaFactory.newInstance(null, SCHEMA_FACTORY_CLASSNAME, this.getClass().getClassLoader())); } /* @@ -161,15 +170,17 @@ public class SchemaFactoryTest { * java.lang.String factoryClassName, java.lang.ClassLoader classLoader) * schemaLanguage is empty , should throw IllegalArgumentException */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testNewInstanceWithEmptySchemaLanguage() { - SchemaFactory.newInstance("", SCHEMA_FACTORY_CLASSNAME, this.getClass().getClassLoader()); + assertThrows( + IllegalArgumentException.class, + () -> SchemaFactory.newInstance("", SCHEMA_FACTORY_CLASSNAME, this.getClass().getClassLoader())); } - @Test(expectedExceptions = SAXParseException.class) - public void testNewSchemaDefault() throws SAXException, IOException { - validate(sf.newSchema()); + @Test + public void testNewSchemaDefault() { + assertThrows(SAXParseException.class, () -> validate(sf.newSchema())); } @Test @@ -177,12 +188,11 @@ public class SchemaFactoryTest { validate(sf.newSchema(new File(XML_DIR + "test.xsd"))); } - @Test(expectedExceptions = NullPointerException.class) - public void testNewSchemaWithNullFile() throws SAXException { - sf.newSchema((File) null); + @Test + public void testNewSchemaWithNullFile() { + assertThrows(NullPointerException.class, () -> sf.newSchema((File) null)); } - @DataProvider(name = "valid-source") public Object[][] getValidSource() throws XMLStreamException { return new Object[][] { { streamSource(xsd1) }, @@ -193,43 +203,42 @@ public class SchemaFactoryTest { } - @Test(dataProvider = "valid-source") + @ParameterizedTest + @MethodSource("getValidSource") public void testNewSchemaWithValidSource(Source schema) throws SAXException, IOException { validate(sf.newSchema(schema)); } - @DataProvider(name = "invalid-source") - public Object[][] getInvalidSource() { + public static Object[][] getInvalidSource() { return new Object[][] { { nullStreamSource() }, { nullSaxSource() } }; } - @Test(dataProvider = "invalid-source", expectedExceptions = SAXParseException.class) - public void testNewSchemaWithInvalidSource(Source schema) throws SAXException { - sf.newSchema(schema); + @ParameterizedTest + @MethodSource("getInvalidSource") + public void testNewSchemaWithInvalidSource(Source schema) { + assertThrows(SAXParseException.class, () -> sf.newSchema(schema)); } - @Test(expectedExceptions = NullPointerException.class) - public void testNewSchemaWithNullSource() throws SAXException { - sf.newSchema((Source)null); + @Test + public void testNewSchemaWithNullSource() { + assertThrows(NullPointerException.class, () -> sf.newSchema((Source) null)); } - @DataProvider(name = "valid-sources") public Object[][] getValidSources() { return new Object[][] { { streamSource(xsd1), streamSource(xsd2) }, { saxSource(xsd1), saxSource(xsd2) }, { domSource(xsdDoc1), domSource(xsdDoc2) } }; - } - @Test(dataProvider = "valid-sources") + @ParameterizedTest + @MethodSource("getValidSources") public void testNewSchemaWithValidSourceArray(Source schema1, Source schema2) throws SAXException, IOException { validate(sf.newSchema(new Source[] { schema1, schema2 })); } - @DataProvider(name = "invalid-sources") public Object[][] getInvalidSources() { return new Object[][] { { streamSource(xsd1), nullStreamSource() }, @@ -238,12 +247,12 @@ public class SchemaFactoryTest { { nullSaxSource(), nullSaxSource() } }; } - @Test(dataProvider = "invalid-sources", expectedExceptions = SAXParseException.class) - public void testNewSchemaWithInvalidSourceArray(Source schema1, Source schema2) throws SAXException { - sf.newSchema(new Source[] { schema1, schema2 }); + @ParameterizedTest + @MethodSource("getInvalidSources") + public void testNewSchemaWithInvalidSourceArray(Source schema1, Source schema2) { + assertThrows(SAXParseException.class, () -> sf.newSchema(new Source[] { schema1, schema2 })); } - @DataProvider(name = "null-sources") public Object[][] getNullSources() { return new Object[][] { { new Source[] { domSource(xsdDoc1), null } }, @@ -252,14 +261,15 @@ public class SchemaFactoryTest { } - @Test(dataProvider = "null-sources", expectedExceptions = NullPointerException.class) - public void testNewSchemaWithNullSourceArray(Source[] schemas) throws SAXException { - sf.newSchema(schemas); + @ParameterizedTest + @MethodSource("getNullSources") + public void testNewSchemaWithNullSourceArray(Source[] schemas) { + assertThrows(NullPointerException.class, () -> sf.newSchema(schemas)); } - @Test(expectedExceptions = NullPointerException.class) - public void testNewSchemaWithNullUrl() throws SAXException { - sf.newSchema((URL) null); + @Test + public void testNewSchemaWithNullUrl() { + assertThrows(NullPointerException.class, () -> sf.newSchema((URL) null)); } @@ -270,70 +280,66 @@ public class SchemaFactoryTest { ErrorHandler handler = new MyErrorHandler(); sf.setErrorHandler(handler); - assertSame(sf.getErrorHandler(), handler); + assertSame(handler, sf.getErrorHandler()); sf.setErrorHandler(null); assertNull(sf.getErrorHandler()); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testGetUnrecognizedProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetUnrecognizedProperty() { SchemaFactory sf = newSchemaFactory(); - sf.getProperty(UNRECOGNIZED_NAME); - + assertThrows(SAXNotRecognizedException.class, () -> sf.getProperty(UNRECOGNIZED_NAME)); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testSetUnrecognizedProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetUnrecognizedProperty() { SchemaFactory sf = newSchemaFactory(); - sf.setProperty(UNRECOGNIZED_NAME, "test"); + assertThrows(SAXNotRecognizedException.class, () -> sf.setProperty(UNRECOGNIZED_NAME, "test")); } - @Test(expectedExceptions = NullPointerException.class) - public void testGetNullProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetNullProperty() { SchemaFactory sf = newSchemaFactory(); assertNotNull(sf); - sf.getProperty(null); - + assertThrows(NullPointerException.class, () -> sf.getProperty(null)); } - @Test(expectedExceptions = NullPointerException.class) - public void testSetNullProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetNullProperty() { SchemaFactory sf = newSchemaFactory(); assertNotNull(sf); - sf.setProperty(null, "test"); + assertThrows(NullPointerException.class, () -> sf.setProperty(null, "test")); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testGetUnrecognizedFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetUnrecognizedFeature() { SchemaFactory sf = newSchemaFactory(); - sf.getFeature(UNRECOGNIZED_NAME); + assertThrows(SAXNotRecognizedException.class, () -> sf.getFeature(UNRECOGNIZED_NAME)); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testSetUnrecognizedFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetUnrecognizedFeature() { SchemaFactory sf = newSchemaFactory(); - sf.setFeature(UNRECOGNIZED_NAME, true); + assertThrows(SAXNotRecognizedException.class, () -> sf.setFeature(UNRECOGNIZED_NAME, true)); } - @Test(expectedExceptions = NullPointerException.class) - public void testGetNullFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetNullFeature() { SchemaFactory sf = newSchemaFactory(); assertNotNull(sf); - sf.getFeature(null); - + assertThrows(NullPointerException.class, () -> sf.getFeature(null)); } - @Test(expectedExceptions = NullPointerException.class) - public void testSetNullFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetNullFeature() { SchemaFactory sf = newSchemaFactory(); assertNotNull(sf); - sf.setFeature(null, true); + assertThrows(NullPointerException.class, () -> sf.setFeature(null, true)); } - @DataProvider(name = "source-feature") - public Object[][] getSourceFeature() { + public static Object[][] getSourceFeature() { return new Object[][] { { StreamSource.FEATURE }, { SAXSource.FEATURE }, @@ -346,7 +352,8 @@ public class SchemaFactoryTest { * Return true for each of the JAXP Source features to indicate that this * SchemaFactory supports all of the built-in JAXP Source types. */ - @Test(dataProvider = "source-feature") + @ParameterizedTest + @MethodSource("getSourceFeature") public void testSourceFeatureGet(String sourceFeature) throws Exception { assertTrue(newSchemaFactory().getFeature(sourceFeature)); } @@ -355,46 +362,52 @@ public class SchemaFactoryTest { * JAXP Source features are read-only because this SchemaFactory always * supports all JAXP Source types. */ - @Test(dataProvider = "source-feature", expectedExceptions = SAXNotSupportedException.class) - public void testSourceFeatureSet(String sourceFeature) throws Exception { - newSchemaFactory().setFeature(sourceFeature, false); + @ParameterizedTest + @MethodSource("getSourceFeature") + public void testSourceFeatureSet(String sourceFeature) { + assertThrows( + SAXNotSupportedException.class, + () -> newSchemaFactory().setFeature(sourceFeature, false)); } - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testInvalidSchemaLanguage() { final String INVALID_SCHEMA_LANGUAGE = "http://relaxng.org/ns/structure/1.0"; - SchemaFactory.newInstance(INVALID_SCHEMA_LANGUAGE); + assertThrows( + IllegalArgumentException.class, + () -> SchemaFactory.newInstance(INVALID_SCHEMA_LANGUAGE)); } - @Test(expectedExceptions = NullPointerException.class) + @Test public void testNullSchemaLanguage() { - SchemaFactory.newInstance(null); + assertThrows(NullPointerException.class, () -> SchemaFactory.newInstance(null)); } private void validate(Schema schema) throws SAXException, IOException { schema.newValidator().validate(new StreamSource(new ByteArrayInputStream(xml))); } - private InputStream newInputStream(byte[] xsd) { + + private static InputStream newInputStream(byte[] xsd) { return new ByteArrayInputStream(xsd); } - private Source streamSource(byte[] xsd) { + private static Source streamSource(byte[] xsd) { return new StreamSource(newInputStream(xsd)); } - private Source nullStreamSource() { + private static Source nullStreamSource() { return new StreamSource((InputStream) null); } - private Source saxSource(byte[] xsd) { + private static Source saxSource(byte[] xsd) { return new SAXSource(new InputSource(newInputStream(xsd))); } - private Source nullSaxSource() { + private static Source nullSaxSource() { return new SAXSource(new InputSource((InputStream) null)); } - private Source domSource(Document xsdDoc) { + private static Source domSource(Document xsdDoc) { return new DOMSource(xsdDoc); } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/TypeInfoProviderTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/TypeInfoProviderTest.java index b8c7a2d54e7..d4802728b5b 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/TypeInfoProviderTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/TypeInfoProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -22,15 +22,12 @@ */ package javax.xml.validation.ptests; -import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.filenameToURL; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; - -import java.io.File; -import java.io.IOException; +import org.junit.jupiter.api.Test; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; @@ -38,18 +35,20 @@ import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.TypeInfoProvider; import javax.xml.validation.ValidatorHandler; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; -import org.testng.annotations.Test; -import org.xml.sax.Attributes; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.DefaultHandler; +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.validation.ptests.TypeInfoProviderTest + * @run junit/othervm javax.xml.validation.ptests.TypeInfoProviderTest * @summary test ValidatorHandler.getTypeInfoProvider() */ public class TypeInfoProviderTest { @@ -65,7 +64,8 @@ public class TypeInfoProviderTest { MyDefaultHandler myDefaultHandler = new MyDefaultHandler(); validatorHandler.setContentHandler(myDefaultHandler); - InputSource is = new InputSource(filenameToURL(XML_DIR + "shiporder11.xml")); + String xmlPathUri = Path.of(XML_DIR).resolve("shiporder11.xml").toUri().toASCIIString(); + InputSource is = new InputSource(xmlPathUri); SAXParserFactory parserFactory = SAXParserFactory.newInstance(); parserFactory.setNamespaceAware(true); @@ -77,13 +77,13 @@ public class TypeInfoProviderTest { private class MyDefaultHandler extends DefaultHandler { - public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { + public void startElement(String namespaceURI, String localName, String qName, Attributes atts) { TypeInfoProvider typeInfoProvider = validatorHandler.getTypeInfoProvider(); int index = atts.getIndex("orderid"); if (index != -1) { System.out.println(" Index " + index); System.out.println(" ElementType " + typeInfoProvider.getElementTypeInfo().getTypeName()); - assertEquals(typeInfoProvider.getAttributeTypeInfo(index).getTypeName(), "string"); + assertEquals("string", typeInfoProvider.getAttributeTypeInfo(index).getTypeName()); assertTrue(typeInfoProvider.isSpecified(index)); assertFalse(typeInfoProvider.isIdAttribute(index)); } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorHandlerTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorHandlerTest.java index 2bbed60ba37..b6c66070417 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorHandlerTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorHandlerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -22,37 +22,39 @@ */ package javax.xml.validation.ptests; -import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertTrue; - -import java.io.File; - -import javax.xml.validation.Schema; -import javax.xml.validation.SchemaFactory; -import javax.xml.validation.ValidatorHandler; - -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.xml.sax.ContentHandler; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; import org.xml.sax.helpers.DefaultHandler; +import org.junit.jupiter.api.BeforeAll; + +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import javax.xml.validation.ValidatorHandler; +import java.io.File; + +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.validation.ptests.ValidatorHandlerTest + * @run junit/othervm javax.xml.validation.ptests.ValidatorHandlerTest * @summary Class containing the test cases for ValidatorHandler API */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class ValidatorHandlerTest { - @BeforeClass + @BeforeAll public void setup() throws SAXException { schema = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI).newSchema(new File(XML_DIR + "test.xsd")); @@ -66,60 +68,57 @@ public class ValidatorHandlerTest { ErrorHandler handler = new MyErrorHandler(); validatorHandler.setErrorHandler(handler); - assertSame(validatorHandler.getErrorHandler(), handler); - + assertSame(handler, validatorHandler.getErrorHandler()); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testGetUnrecognizedProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetUnrecognizedProperty() { ValidatorHandler validatorHandler = getValidatorHandler(); - validatorHandler.getProperty(FEATURE_NAME); - + assertThrows(SAXNotRecognizedException.class, () -> validatorHandler.getProperty(FEATURE_NAME)); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testSetUnrecognizedProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetUnrecognizedProperty() { ValidatorHandler validatorHandler = getValidatorHandler(); - validatorHandler.setProperty(FEATURE_NAME, "test"); + assertThrows(SAXNotRecognizedException.class, () -> validatorHandler.setProperty(FEATURE_NAME, "test")); } - @Test(expectedExceptions = NullPointerException.class) - public void testGetNullProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetNullProperty() { ValidatorHandler validatorHandler = getValidatorHandler(); assertNotNull(validatorHandler); - validatorHandler.getProperty(null); + assertThrows(NullPointerException.class, () -> validatorHandler.getProperty(null)); } - @Test(expectedExceptions = NullPointerException.class) - public void testSetNullProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetNullProperty() { ValidatorHandler validatorHandler = getValidatorHandler(); assertNotNull(validatorHandler); - validatorHandler.setProperty(null, "test"); + assertThrows(NullPointerException.class, () -> validatorHandler.setProperty(null, "test")); } + @Test public void testFeature() throws SAXNotRecognizedException, SAXNotSupportedException { ValidatorHandler validatorHandler = getValidatorHandler(); assertFalse(validatorHandler.getFeature(FEATURE_NAME), "The feature should be false by default."); validatorHandler.setFeature(FEATURE_NAME, true); assertTrue(validatorHandler.getFeature(FEATURE_NAME), "The feature should be false by default."); - } - @Test(expectedExceptions = NullPointerException.class) - public void testGetNullFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetNullFeature() { ValidatorHandler validatorHandler = getValidatorHandler(); assertNotNull(validatorHandler); - validatorHandler.getFeature(null); - + assertThrows(NullPointerException.class, () -> validatorHandler.getFeature(null)); } - @Test(expectedExceptions = NullPointerException.class) - public void testSetNullFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetNullFeature() { ValidatorHandler validatorHandler = getValidatorHandler(); assertNotNull(validatorHandler); - validatorHandler.setFeature(null, true); + assertThrows(NullPointerException.class, () -> validatorHandler.setFeature(null, true)); } @Test @@ -129,11 +128,10 @@ public class ValidatorHandlerTest { ContentHandler handler = new DefaultHandler(); validatorHandler.setContentHandler(handler); - assertSame(validatorHandler.getContentHandler(), handler); + assertSame(handler, validatorHandler.getContentHandler()); validatorHandler.setContentHandler(null); assertNull(validatorHandler.getContentHandler()); - } private ValidatorHandler getValidatorHandler() { diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorTest.java index 6dbd38a7fa1..f2cbeb1ebeb 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -22,15 +22,18 @@ */ package javax.xml.validation.ptests; -import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.filenameToURL; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertSame; - -import java.io.File; -import java.io.IOException; +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 org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -44,33 +47,33 @@ import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.xml.sax.ErrorHandler; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.SAXNotRecognizedException; -import org.xml.sax.SAXNotSupportedException; -import org.xml.sax.helpers.DefaultHandler; +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.validation.ptests.ValidatorTest + * @run junit/othervm javax.xml.validation.ptests.ValidatorTest * @summary Class containing the test cases for Validator API */ +@TestInstance(Lifecycle.PER_CLASS) public class ValidatorTest { - @BeforeClass + @BeforeAll public void setup() throws SAXException, IOException, ParserConfigurationException { schema = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI).newSchema(new File(XML_DIR + "test.xsd")); assertNotNull(schema); - xmlFileUri = filenameToURL(XML_DIR + "test.xml"); + xmlFileUri = Paths.get(XML_DIR).resolve("test.xml").toUri().toASCIIString(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); @@ -84,11 +87,11 @@ public class ValidatorTest { validator.validate(getStreamSource()); } - @Test(expectedExceptions = NullPointerException.class) - public void testValidateNullSource() throws SAXException, IOException { + @Test + public void testValidateNullSource() { Validator validator = getValidator(); assertNotNull(validator); - validator.validate(null); + assertThrows(NullPointerException.class, () -> validator.validate(null)); } @Test @@ -98,11 +101,10 @@ public class ValidatorTest { ErrorHandler mh = new MyErrorHandler(); validator.setErrorHandler(mh); - assertSame(validator.getErrorHandler(), mh); + assertSame(mh, validator.getErrorHandler()); } - @DataProvider(name = "source-result") public Object[][] getSourceAndResult() { return new Object[][] { { getStreamSource(), null }, @@ -112,66 +114,65 @@ public class ValidatorTest { { getDOMSource(), null } }; } - @Test(dataProvider = "source-result") + @ParameterizedTest + @MethodSource("getSourceAndResult") public void testValidateWithResult(Source source, Result result) throws SAXException, IOException { Validator validator = getValidator(); validator.validate(source, result); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testGetUnrecognizedProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetUnrecognizedProperty() { Validator validator = getValidator(); - validator.getProperty(UNRECOGNIZED_NAME); + assertThrows(SAXNotRecognizedException.class, () -> validator.getProperty(UNRECOGNIZED_NAME)); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testSetUnrecognizedProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetUnrecognizedProperty() { Validator validator = getValidator(); - validator.setProperty(UNRECOGNIZED_NAME, "test"); + assertThrows(SAXNotRecognizedException.class, () -> validator.setProperty(UNRECOGNIZED_NAME, "test")); } - @Test(expectedExceptions = NullPointerException.class) - public void testGetNullProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetNullProperty() { Validator validator = getValidator(); assertNotNull(validator); - validator.getProperty(null); - + assertThrows(NullPointerException.class, () -> validator.getProperty(null)); } - @Test(expectedExceptions = NullPointerException.class) - public void testSetNullProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetNullProperty() { Validator validator = getValidator(); assertNotNull(validator); - validator.setProperty(null, "test"); + assertThrows(NullPointerException.class, () -> validator.setProperty(null, "test")); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testGetUnrecognizedFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetUnrecognizedFeature() { Validator validator = getValidator(); - validator.getFeature(UNRECOGNIZED_NAME); + assertThrows(SAXNotRecognizedException.class, () -> validator.getFeature(UNRECOGNIZED_NAME)); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testSetUnrecognizedFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetUnrecognizedFeature() { Validator validator = getValidator(); - validator.setFeature(UNRECOGNIZED_NAME, true); + assertThrows(SAXNotRecognizedException.class, () -> validator.setFeature(UNRECOGNIZED_NAME, true)); } - @Test(expectedExceptions = NullPointerException.class) - public void testGetNullFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetNullFeature() { Validator validator = getValidator(); assertNotNull(validator); - validator.getFeature(null); - + assertThrows(NullPointerException.class, () -> validator.getFeature(null)); } - @Test(expectedExceptions = NullPointerException.class) - public void testSetNullFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetNullFeature() { Validator validator = getValidator(); assertNotNull(validator); - validator.setFeature(null, true); + assertThrows(NullPointerException.class, () -> validator.setFeature(null, true)); } private Validator getValidator() { @@ -204,5 +205,4 @@ public class ValidatorTest { private String xmlFileUri; private Schema schema; private Document xmlDoc; - } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathEvaluationResultTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathEvaluationResultTest.java index 5daf8adcfa6..81a9fdfa099 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathEvaluationResultTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathEvaluationResultTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -23,34 +23,32 @@ package javax.xml.xpath.ptests; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.w3c.dom.Node; import javax.xml.namespace.QName; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathEvaluationResult; import javax.xml.xpath.XPathNodes; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; /* * @test * @bug 8183266 * @summary verifies the specification of the XPathEvaluationResult API * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.xpath.ptests.XPathEvaluationResultTest + * @run junit/othervm javax.xml.xpath.ptests.XPathEvaluationResultTest */ public class XPathEvaluationResultTest { /* * Test getQNameType returns QName type for supported types and Number subtypes */ - @Test(dataProvider = "supportedTypes") + @ParameterizedTest + @MethodSource("getSupportedTypes") public void testQNameTypeSupportedTypes(QName expectedQName, Class type) { QName qName = XPathEvaluationResult.XPathResultType.getQNameType(type); assertNotNull(qName); @@ -60,53 +58,16 @@ public class XPathEvaluationResultTest { /* * Test getQNameType returns null when type is not supported */ - @Test(dataProvider = "unsupportedTypes") - public void testQNameTypeUnsupportedTypes(Class type) { - QName qName = XPathEvaluationResult.XPathResultType.getQNameType(type); - assertNull(qName); - } - - /* - * Test getQNameType is null safe - */ - @Test - public void testQNameTypeNullType() { - QName qName = XPathEvaluationResult.XPathResultType.getQNameType(null); - assertNull(qName); - } - - /* - * DataProvider: Class types supported - */ - @DataProvider(name = "supportedTypes") - public Object[][] getSupportedTypes() { - return new Object[][]{ - {XPathConstants.STRING, String.class}, - {XPathConstants.BOOLEAN, Boolean.class}, - {XPathConstants.NODESET, XPathNodes.class}, - {XPathConstants.NODE, Node.class}, - {XPathConstants.NUMBER, Long.class}, - {XPathConstants.NUMBER, Integer.class}, - {XPathConstants.NUMBER, Double.class}, - {XPathConstants.NUMBER, Number.class} - }; - } - - /* - * DataProvider: Class types not supported - */ - @DataProvider(name = "unsupportedTypes") - public Object[][] getUnsupportedTypes() { - return new Object[][]{ - new Object[]{AtomicInteger.class}, - new Object[]{AtomicLong.class}, - new Object[]{BigDecimal.class}, - new Object[]{BigInteger.class}, - new Object[]{Byte.class}, - new Object[]{Float.class}, - new Object[]{Short.class}, - new Object[]{Character.class}, - new Object[]{StringBuilder.class}, + public static Object[][] getSupportedTypes() { + return new Object[][] { + { XPathConstants.STRING, String.class }, + { XPathConstants.BOOLEAN, Boolean.class }, + { XPathConstants.NODESET, XPathNodes.class }, + { XPathConstants.NODE, Node.class }, + { XPathConstants.NUMBER, Long.class }, + { XPathConstants.NUMBER, Integer.class }, + { XPathConstants.NUMBER, Double.class }, + { XPathConstants.NUMBER, Number.class } }; } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathExpressionTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathExpressionTest.java index c63b161aae9..bdd21937504 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathExpressionTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathExpressionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -23,32 +23,33 @@ package javax.xml.xpath.ptests; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +import javax.xml.XMLConstants; +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + import static javax.xml.xpath.XPathConstants.BOOLEAN; import static javax.xml.xpath.XPathConstants.NODE; import static javax.xml.xpath.XPathConstants.NODESET; import static javax.xml.xpath.XPathConstants.NUMBER; import static javax.xml.xpath.XPathConstants.STRING; import static javax.xml.xpath.ptests.XPathTestConst.XML_DIR; -import static org.testng.Assert.assertEquals; - -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - -import javax.xml.XMLConstants; -import javax.xml.namespace.QName; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.xpath.XPath; -import javax.xml.xpath.XPathExpressionException; -import javax.xml.xpath.XPathFactory; - -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Class containing the test cases for XPathExpression API. @@ -56,7 +57,7 @@ import org.xml.sax.InputSource; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.xpath.ptests.XPathExpressionTest + * @run junit/othervm javax.xml.xpath.ptests.XPathExpressionTest */ public class XPathExpressionTest { /** @@ -77,7 +78,7 @@ public class XPathExpressionTest { /** * XML File Path. */ - private static final Path XML_PATH = Paths.get(XML_DIR + "widgets.xml"); + private static final Path XML_PATH = XML_DIR.resolve("widgets.xml"); /** * An expression name which locate at "/widgets/widget[@name='a']/@quantity" @@ -93,7 +94,7 @@ public class XPathExpressionTest { * Create Document object and XPath object for every time * @throws Exception If any errors occur. */ - @BeforeTest + @BeforeEach public void setup() throws Exception { document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(XML_PATH.toFile()); xpath = XPathFactory.newInstance().newXPath(); @@ -102,303 +103,190 @@ public class XPathExpressionTest { /** * Test for evaluate(java.lang.Object item,QName returnType)throws * XPathExpressionException. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ @Test public void testCheckXPathExpression01() throws XPathExpressionException { - assertEquals(xpath.compile(EXPRESSION_NAME_A). - evaluate(document, STRING), "6"); + assertEquals("6", xpath.compile(EXPRESSION_NAME_A). + evaluate(document, STRING)); } /** * evaluate(java.lang.Object item,QName returnType) throws NPE if input * source is null. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPathExpression02() throws XPathExpressionException { - xpath.compile(EXPRESSION_NAME_A).evaluate(null, STRING); + XPathExpression expr = xpath.compile(EXPRESSION_NAME_A); + assertThrows(NullPointerException.class, () -> expr.evaluate(null, STRING)); } /** * evaluate(java.lang.Object item,QName returnType) throws NPE if returnType * is null. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPathExpression03() throws XPathExpressionException { - xpath.compile(EXPRESSION_NAME_A).evaluate(document, null); + XPathExpression expr = xpath.compile(EXPRESSION_NAME_A); + assertThrows(NullPointerException.class, () -> expr.evaluate(document, null)); } /** * Test for method evaluate(java.lang.Object item,QName returnType).If a * request is made to evaluate the expression in the absence of a context * item, simple expressions, such as "1+1", can be evaluated. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ @Test public void testCheckXPathExpression04() throws XPathExpressionException { - assertEquals(xpath.compile("1+1").evaluate(document, STRING), "2"); + assertEquals("2", xpath.compile("1+1").evaluate(document, STRING)); } /** * evaluate(java.lang.Object item,QName returnType) throws IAE If returnType * is not one of the types defined in XPathConstants. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testCheckXPathExpression05() throws XPathExpressionException { - xpath.compile(EXPRESSION_NAME_A).evaluate(document, TEST_QNAME); + XPathExpression expr = xpath.compile(EXPRESSION_NAME_A); + assertThrows(IllegalArgumentException.class, () -> expr.evaluate(document, TEST_QNAME)); } /** * evaluate(java.lang.Object item,QName returnType) return correct boolean * value if returnType is Boolean. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ @Test public void testCheckXPathExpression06() throws XPathExpressionException { - assertEquals(xpath.compile(EXPRESSION_NAME_A). - evaluate(document, BOOLEAN), true); + assertEquals(true, xpath.compile(EXPRESSION_NAME_A). + evaluate(document, BOOLEAN)); } /** * evaluate(java.lang.Object item,QName returnType) return correct boolean * value if returnType is Boolean. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ @Test public void testCheckXPathExpression07() throws XPathExpressionException { - assertEquals(xpath.compile(EXPRESSION_NAME_B). - evaluate(document, BOOLEAN), false); + assertEquals(false, xpath.compile(EXPRESSION_NAME_B). + evaluate(document, BOOLEAN)); } /** * evaluate(java.lang.Object item,QName returnType) return correct number * value when return type is Double. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ @Test public void testCheckXPathExpression08() throws XPathExpressionException { - assertEquals(xpath.compile(EXPRESSION_NAME_A). - evaluate(document, NUMBER), 6d); + assertEquals(6d, xpath.compile(EXPRESSION_NAME_A). + evaluate(document, NUMBER)); } /** * evaluate(java.lang.Object item,QName returnType) evaluate an attribute * value which returnType is Node. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ @Test public void testCheckXPathExpression09() throws XPathExpressionException { Attr attr = (Attr) xpath.compile(EXPRESSION_NAME_A). evaluate(document, NODE); - assertEquals(attr.getValue(), "6"); + assertEquals("6", attr.getValue()); } /** * evaluate(java.lang.Object item,QName returnType) evaluate an attribute * value which returnType is NodeList. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ @Test public void testCheckXPathExpression10() throws XPathExpressionException { NodeList nodeList = (NodeList) xpath.compile(EXPRESSION_NAME_A). evaluate(document, NODESET); Attr attr = (Attr) nodeList.item(0); - assertEquals(attr.getValue(), "6"); + assertEquals("6", attr.getValue()); } /** * Test for evaluate(java.lang.Object item) when returnType is left off of * the XPath.evaluate method, all expressions are evaluated to a String * value. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ @Test public void testCheckXPathExpression11() throws XPathExpressionException { - assertEquals(xpath.compile(EXPRESSION_NAME_A).evaluate(document), "6"); - } - - /** - * evaluate(java.lang.Object item) throws NPE if expression is null. - * - * @throws XPathExpressionException If the expression cannot be evaluated. - */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPathExpression12() throws XPathExpressionException { - xpath.compile(null).evaluate(document); + assertEquals("6", xpath.compile(EXPRESSION_NAME_A).evaluate(document)); } /** * evaluate(java.lang.Object item) when a request is made to evaluate the * expression in the absence of a context item, simple expressions, such as * "1+1", can be evaluated. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ @Test public void testCheckXPathExpression13() throws XPathExpressionException { - assertEquals(xpath.compile("1+1").evaluate(document), "2"); + assertEquals("2", xpath.compile("1+1").evaluate(document)); } /** * evaluate(java.lang.Object item) throws NPE if document is null. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPathExpression14() throws XPathExpressionException { - xpath.compile(EXPRESSION_NAME_A).evaluate(null); + XPathExpression expr = xpath.compile(EXPRESSION_NAME_A); + assertThrows(NullPointerException.class, () -> expr.evaluate(null)); } /** * valuate(InputSource source) return a string value if return type is * String. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPathExpression15() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.compile(EXPRESSION_NAME_A). - evaluate(new InputSource(is)), "6"); + assertEquals("6", xpath.compile(EXPRESSION_NAME_A). + evaluate(new InputSource(is))); } } /** * evaluate(InputSource source) throws NPE if input source is null. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPathExpression16() throws XPathExpressionException { - xpath.compile(EXPRESSION_NAME_A).evaluate(null); - } - - /** - * evaluate(InputSource source) throws NPE if expression is null. - * - * @throws Exception If any errors occur. - */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPathExpression17() throws Exception { - try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.compile(null).evaluate(new InputSource(is)); - } - } - - /** - * evaluate(InputSource source) throws XPathExpressionException if - * returnType is String junk characters. - * - * @throws Exception If any errors occur. - */ - @Test(expectedExceptions = XPathExpressionException.class) - public void testCheckXPathExpression18() throws Exception { - try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.compile("-*&").evaluate(new InputSource(is)); - } - } - - /** - * evaluate(InputSource source) throws XPathExpressionException if - * expression is a blank string " ". - * - * @throws Exception If any errors occur. - */ - @Test(expectedExceptions = XPathExpressionException.class) - public void testCheckXPathExpression19() throws Exception { - try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.compile(" ").evaluate(new InputSource(is)); - } + XPathExpression expr = xpath.compile(EXPRESSION_NAME_A); + assertThrows(NullPointerException.class, () -> expr.evaluate(null)); } /** * Test for evaluate(InputSource source,QName returnType) returns a string * value if returnType is String. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPathExpression20() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.compile(EXPRESSION_NAME_A). - evaluate(new InputSource(is), STRING), "6"); + assertEquals("6", xpath.compile(EXPRESSION_NAME_A). + evaluate(new InputSource(is), STRING)); } } /** * evaluate(InputSource source,QName returnType) throws NPE if source is * null. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPathExpression21() throws XPathExpressionException { - xpath.compile(EXPRESSION_NAME_A).evaluate(null, STRING); - } - - /** - * evaluate(InputSource source,QName returnType) throws NPE if expression is - * null. - * - * @throws Exception If any errors occur. - */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPathExpression22() throws Exception { - try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.compile(null).evaluate(new InputSource(is), STRING); - } + XPathExpression expr = xpath.compile(EXPRESSION_NAME_A); + assertThrows(NullPointerException.class, () -> expr.evaluate(null, STRING)); } /** * evaluate(InputSource source,QName returnType) throws NPE if returnType is * null. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPathExpression23() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.compile(EXPRESSION_NAME_A).evaluate(new InputSource(is), null); - } - } - - /** - * evaluate(InputSource source,QName returnType) throws - * XPathExpressionException if expression is junk characters. - * - * @throws Exception If any errors occur. - */ - @Test(expectedExceptions = XPathExpressionException.class) - public void testCheckXPathExpression24() throws Exception { - try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.compile("-*&").evaluate(new InputSource(is), STRING); - } - } - - /** - * evaluate(InputSource source,QName returnType) throws - * XPathExpressionException if expression is blank " ". - * - * @throws Exception If any errors occur. - */ - @Test(expectedExceptions = XPathExpressionException.class) - public void testCheckXPathExpression25() throws Exception { - try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.compile(" ").evaluate(new InputSource(is), STRING); + XPathExpression expr = xpath.compile(EXPRESSION_NAME_A); + InputSource source = new InputSource(is); + assertThrows( + NullPointerException.class, + () -> expr.evaluate(source, null)); } } @@ -406,85 +294,75 @@ public class XPathExpressionTest { * evaluate(InputSource source,QName returnType) throws * IllegalArgumentException if returnType is not one of the types defined * in XPathConstants. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testCheckXPathExpression26() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.compile(EXPRESSION_NAME_A).evaluate(new InputSource(is), TEST_QNAME); + XPathExpression expr = xpath.compile(EXPRESSION_NAME_A); + InputSource source = new InputSource(is); + assertThrows(IllegalArgumentException.class, () -> expr.evaluate(source, TEST_QNAME)); } } /** * evaluate(InputSource source,QName returnType) return a correct boolean * value if returnType is Boolean. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPathExpression27() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.compile(EXPRESSION_NAME_A). - evaluate(new InputSource(is), BOOLEAN), true); + assertEquals(true, xpath.compile(EXPRESSION_NAME_A). + evaluate(new InputSource(is), BOOLEAN)); } } /** * evaluate(InputSource source,QName returnType) return a correct boolean * value if returnType is Boolean. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPathExpression28() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.compile(EXPRESSION_NAME_B). - evaluate(new InputSource(is), BOOLEAN), false); + assertEquals(false, xpath.compile(EXPRESSION_NAME_B). + evaluate(new InputSource(is), BOOLEAN)); } } /** * evaluate(InputSource source,QName returnType) return a correct number * value if returnType is Number. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPathExpression29() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.compile(EXPRESSION_NAME_A). - evaluate(new InputSource(is), NUMBER), 6d); + assertEquals(6d, xpath.compile(EXPRESSION_NAME_A). + evaluate(new InputSource(is), NUMBER)); } } /** * Test for evaluate(InputSource source,QName returnType) returns a node if * returnType is Node. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPathExpression30() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { Attr attr = (Attr) xpath.compile(EXPRESSION_NAME_A). - evaluate(new InputSource(is), NODE); - assertEquals(attr.getValue(), "6"); + evaluate(new InputSource(is), NODE); + assertEquals("6", attr.getValue()); } } /** * Test for evaluate(InputSource source,QName returnType) return a node list * if returnType is NodeList. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPathExpression31() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { NodeList nodeList = (NodeList) xpath.compile(EXPRESSION_NAME_A). - evaluate(new InputSource(is), NODESET); - assertEquals(((Attr) nodeList.item(0)).getValue(), "6"); + evaluate(new InputSource(is), NODESET); + assertEquals("6", ((Attr) nodeList.item(0)).getValue()); } } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathFactoryTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathFactoryTest.java index 561c6923eda..3c276f24908 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathFactoryTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -23,23 +23,24 @@ package javax.xml.xpath.ptests; -import static javax.xml.xpath.XPathConstants.DOM_OBJECT_MODEL; -import static javax.xml.xpath.XPathFactory.DEFAULT_OBJECT_MODEL_URI; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNotSame; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertFalse; +import jaxp.library.JAXPDataProvider; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathFactory; import javax.xml.xpath.XPathFactoryConfigurationException; -import jaxp.library.JAXPDataProvider; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import static javax.xml.xpath.XPathConstants.DOM_OBJECT_MODEL; +import static javax.xml.xpath.XPathFactory.DEFAULT_OBJECT_MODEL_URI; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Class containing the test cases for XPathFactory API. @@ -48,7 +49,8 @@ import org.testng.annotations.Test; * @test * @bug 8169778 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.xpath.ptests.XPathFactoryTest + * @build jaxp.library.JAXPDataProvider + * @run junit/othervm javax.xml.xpath.ptests.XPathFactoryTest */ public class XPathFactoryTest { /** @@ -78,9 +80,11 @@ public class XPathFactoryTest { * * @return a data provider contains XPathFactory instantiation parameters. */ - @DataProvider(name = "parameters") - public Object[][] getValidateParameters() { - return new Object[][] { { VALID_URL, XPATH_FACTORY_CLASSNAME, null }, { VALID_URL, XPATH_FACTORY_CLASSNAME, this.getClass().getClassLoader() } }; + public static Object[][] getValidateParameters() { + return new Object[][] { + { VALID_URL, XPATH_FACTORY_CLASSNAME, null }, + { VALID_URL, XPATH_FACTORY_CLASSNAME, XPathFactoryTest.class.getClassLoader() }, + }; } /** @@ -94,8 +98,8 @@ public class XPathFactoryTest { XPathFactory xpf2 = XPathFactory.newInstance(DEFAULT_OBJECT_MODEL_URI); assertNotSame(xpf1, xpf2, "same instance returned:"); assertSame(xpf1.getClass(), xpf2.getClass(), - "unexpected class mismatch for newDefaultInstance():"); - assertEquals(xpf1.getClass().getName(), DEFAULT_IMPL_CLASS); + "unexpected class mismatch for newDefaultInstance():"); + assertEquals(DEFAULT_IMPL_CLASS, xpf1.getClass().getName()); assertTrue(xpf1.isObjectModelSupported(DEFAULT_OBJECT_MODEL_URI), "isObjectModelSupported(DEFAULT_OBJECT_MODEL_URI):"); assertFalse(xpf1.isObjectModelSupported(INVALID_URL), @@ -107,13 +111,9 @@ public class XPathFactoryTest { * factoryClassName, java.lang.ClassLoader classLoader) factoryClassName * points to correct implementation of javax.xml.xpath.XPathFactory , should * return newInstance of XPathFactory - * - * @param uri - * @param factoryClassName - * @param classLoader - * @throws XPathFactoryConfigurationException */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("getValidateParameters") public void testNewInstance(String uri, String factoryClassName, ClassLoader classLoader) throws XPathFactoryConfigurationException { XPathFactory xpf = XPathFactory.newInstance(uri, factoryClassName, classLoader); XPath xpath = xpf.newXPath(); @@ -123,39 +123,34 @@ public class XPathFactoryTest { /** * Test for XPathFactory.newInstance(java.lang.String uri, java.lang.String * factoryClassName, java.lang.ClassLoader classLoader) - * - * @param factoryClassName - * @param classLoader - * @throws XPathFactoryConfigurationException - * is expected when factoryClassName is null */ - @Test(expectedExceptions = XPathFactoryConfigurationException.class, dataProvider = "new-instance-neg", dataProviderClass = JAXPDataProvider.class) - public void testNewInstanceWithNullFactoryClassName(String factoryClassName, ClassLoader classLoader) throws XPathFactoryConfigurationException { - XPathFactory.newInstance(VALID_URL, factoryClassName, classLoader); + @ParameterizedTest + @MethodSource("jaxp.library.JAXPDataProvider#newInstanceNeg") + public void testNewInstanceWithNullFactoryClassName(String factoryClassName, ClassLoader classLoader) { + assertThrows( + XPathFactoryConfigurationException.class, + () -> XPathFactory.newInstance(VALID_URL, factoryClassName, classLoader)); } /** * Test for XPathFactory.newInstance(java.lang.String uri, java.lang.String * factoryClassName, java.lang.ClassLoader classLoader) uri is null , should * throw NPE - * - * @throws XPathFactoryConfigurationException */ - @Test(expectedExceptions = NullPointerException.class) - public void testNewInstanceWithNullUri() throws XPathFactoryConfigurationException { - XPathFactory.newInstance(null, XPATH_FACTORY_CLASSNAME, this.getClass().getClassLoader()); + @Test + public void testNewInstanceWithNullUri() { + assertThrows( + NullPointerException.class, + () -> XPathFactory.newInstance(null, XPATH_FACTORY_CLASSNAME, this.getClass().getClassLoader())); } /** * Test for XPathFactory.newInstance(java.lang.String uri, java.lang.String * factoryClassName, java.lang.ClassLoader classLoader) - * - * @throws IllegalArgumentException - * is expected when uri is empty */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testNewInstanceWithEmptyUri() throws XPathFactoryConfigurationException { - XPathFactory.newInstance("", XPATH_FACTORY_CLASSNAME, this.getClass().getClassLoader()); + @Test + public void testNewInstanceWithEmptyUri() { + assertThrows(IllegalArgumentException.class, () -> XPathFactory.newInstance("", XPATH_FACTORY_CLASSNAME, this.getClass().getClassLoader())); } /** @@ -169,24 +164,20 @@ public class XPathFactoryTest { /** * XPathFactory.newInstance(String uri) throws NPE if uri is null. * - * @throws XPathFactoryConfigurationException If the specified object model - * is unavailable, or if there is a configuration error. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPathFactory02() throws XPathFactoryConfigurationException { - XPathFactory.newInstance(null); + @Test + public void testCheckXPathFactory02() { + assertThrows(NullPointerException.class, () -> XPathFactory.newInstance(null)); } /** * XPathFactory.newInstance(String uri) throws XPFCE if uri is just a blank * string. * - * @throws XPathFactoryConfigurationException If the specified object model - * is unavailable, or if there is a configuration error. */ - @Test(expectedExceptions = XPathFactoryConfigurationException.class) - public void testCheckXPathFactory03() throws XPathFactoryConfigurationException { - XPathFactory.newInstance(" "); + @Test + public void testCheckXPathFactory03() { + assertThrows(XPathFactoryConfigurationException.class, () -> XPathFactory.newInstance(" ")); } /** @@ -205,12 +196,10 @@ public class XPathFactoryTest { * Test for constructor - XPathFactory.newInstance(String uri) with invalid * url - "http://java.sun.com/jaxp/xpath/dom1". * - * @throws XPathFactoryConfigurationException If the specified object model - * is unavailable, or if there is a configuration error. */ - @Test(expectedExceptions = XPathFactoryConfigurationException.class) - public void testCheckXPathFactory05() throws XPathFactoryConfigurationException { - XPathFactory.newInstance(INVALID_URL); + @Test + public void testCheckXPathFactory05() { + assertThrows(XPathFactoryConfigurationException.class, () -> XPathFactory.newInstance(INVALID_URL)); } /** diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathFunctionResolverTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathFunctionResolverTest.java index 07d439e5aa8..f74540d8f68 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathFunctionResolverTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathFunctionResolverTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -23,14 +23,15 @@ package javax.xml.xpath.ptests; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Class containing the test cases for XPathFunctionResolver. @@ -38,7 +39,7 @@ import org.testng.annotations.Test; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.xpath.ptests.XPathFunctionResolverTest + * @run junit/othervm javax.xml.xpath.ptests.XPathFunctionResolverTest */ public class XPathFunctionResolverTest { /** @@ -50,7 +51,7 @@ public class XPathFunctionResolverTest { * Create XPath object before every test. Make sure function resolver has * been set for XPath object. */ - @BeforeTest + @BeforeEach public void setup() { xpath = XPathFactory.newInstance().newXPath(); if (xpath.getXPathFunctionResolver() == null) { @@ -65,17 +66,16 @@ public class XPathFunctionResolverTest { */ @Test public void testCheckXPathFunctionResolver01() throws XPathExpressionException { - assertEquals(xpath.evaluate("round(1.7)", (Object)null), "2"); + assertEquals("2", xpath.evaluate("round(1.7)", (Object) null)); } /** * Test for resolveFunction(QName functionName,int arity); evaluate throws * NPE if functionName is null. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPathFunctionResolver02() throws XPathExpressionException { - assertEquals(xpath.evaluate(null, "5"), "2"); + @Test + public void testCheckXPathFunctionResolver02() { + assertThrows(NullPointerException.class, () -> xpath.evaluate(null, "5")); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathTest.java index e2eb0d6a03a..7bbfaae6b76 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -23,21 +23,12 @@ package javax.xml.xpath.ptests; -import static javax.xml.xpath.XPathConstants.BOOLEAN; -import static javax.xml.xpath.XPathConstants.NODE; -import static javax.xml.xpath.XPathConstants.NODESET; -import static javax.xml.xpath.XPathConstants.NUMBER; -import static javax.xml.xpath.XPathConstants.STRING; -import static javax.xml.xpath.ptests.XPathTestConst.XML_DIR; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.assertNull; - -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Iterator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; @@ -46,13 +37,21 @@ import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Iterator; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; +import static javax.xml.xpath.XPathConstants.BOOLEAN; +import static javax.xml.xpath.XPathConstants.NODE; +import static javax.xml.xpath.XPathConstants.NODESET; +import static javax.xml.xpath.XPathConstants.NUMBER; +import static javax.xml.xpath.XPathConstants.STRING; +import static javax.xml.xpath.ptests.XPathTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Class containing the test cases for XPath API. @@ -60,7 +59,7 @@ import org.xml.sax.InputSource; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.xpath.ptests.XPathTest + * @run junit/othervm javax.xml.xpath.ptests.XPathTest */ public class XPathTest { /** @@ -81,7 +80,7 @@ public class XPathTest { /** * XML File Path. */ - private static final Path XML_PATH = Paths.get(XML_DIR + "widgets.xml"); + private static final Path XML_PATH = XML_DIR.resolve("widgets.xml"); /** * An expression name which locate at "/widgets/widget[@name='a']/@quantity" @@ -97,7 +96,7 @@ public class XPathTest { * Create Document object and XPath object for every time * @throws Exception If any errors occur. */ - @BeforeTest + @BeforeEach public void setup() throws Exception { document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(XML_PATH.toFile()); xpath = XPathFactory.newInstance().newXPath(); @@ -111,7 +110,7 @@ public class XPathTest { */ @Test public void testCheckXPath01() throws XPathExpressionException { - assertEquals(xpath.evaluate(EXPRESSION_NAME_A, document, STRING), "6"); + assertEquals("6", xpath.evaluate(EXPRESSION_NAME_A, document, STRING)); } @@ -123,7 +122,7 @@ public class XPathTest { */ @Test public void testCheckXPath02() throws XPathExpressionException { - assertEquals(xpath.compile(EXPRESSION_NAME_A).evaluate(document, STRING), "6"); + assertEquals("6", xpath.compile(EXPRESSION_NAME_A).evaluate(document, STRING)); } /** @@ -135,40 +134,37 @@ public class XPathTest { */ @Test public void testCheckXPath03() throws XPathExpressionException { - assertEquals(xpath.evaluate(EXPRESSION_NAME_A, document), "6"); + assertEquals("6", xpath.evaluate(EXPRESSION_NAME_A, document)); } /** * Test for XPath.compile(java.lang.String expression). If expression is * null, should throw NPE. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPath04() throws XPathExpressionException { - xpath.compile(null); + @Test + public void testCheckXPath04() { + assertThrows(NullPointerException.class, () -> xpath.compile(null)); } /** * Test for XPath.compile(java.lang.String expression). If expression cannot * be compiled junk characters, should throw XPathExpressionException. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = XPathExpressionException.class) - public void testCheckXPath05() throws XPathExpressionException { - xpath.compile("-*&"); + @Test + public void testCheckXPath05() { + assertThrows(XPathExpressionException.class, () -> xpath.compile("-*&")); } /** * Test for XPath.compile(java.lang.String expression). If expression is * blank, should throw XPathExpressionException * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = XPathExpressionException.class) - public void testCheckXPath06() throws XPathExpressionException { - xpath.compile(" "); + @Test + public void testCheckXPath06() { + assertThrows(XPathExpressionException.class, () -> xpath.compile(" ")); } /** @@ -179,7 +175,7 @@ public class XPathTest { */ @Test public void testCheckXPath07() throws XPathExpressionException { - assertEquals(xpath.compile(EXPRESSION_NAME_B).evaluate(document, STRING), ""); + assertEquals("", xpath.compile(EXPRESSION_NAME_B).evaluate(document, STRING)); } @@ -187,33 +183,30 @@ public class XPathTest { * Test for XPath.evaluate(java.lang.String expression, java.lang.Object * item, QName returnType). If String expression is null, should throw NPE. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPath08() throws XPathExpressionException { - xpath.evaluate(null, document, STRING); + @Test + public void testCheckXPath08() { + assertThrows(NullPointerException.class, () -> xpath.evaluate(null, document, STRING)); } /** * Test for XPath.evaluate(java.lang.String expression, java.lang.Object * item, QName returnType). If item is null, should throw NPE. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPath09() throws XPathExpressionException { - xpath.evaluate(EXPRESSION_NAME_A, null, STRING); + @Test + public void testCheckXPath09() { + assertThrows(NullPointerException.class, () -> xpath.evaluate(EXPRESSION_NAME_A, null, STRING)); } /** * Test for XPath.evaluate(java.lang.String expression, java.lang.Object * item, QName returnType). If returnType is null, should throw NPE. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPath10() throws XPathExpressionException { - xpath.evaluate(EXPRESSION_NAME_A, document, null); + @Test + public void testCheckXPath10() { + assertThrows(NullPointerException.class, () -> xpath.evaluate(EXPRESSION_NAME_A, document, null)); } /** @@ -226,7 +219,7 @@ public class XPathTest { */ @Test public void testCheckXPath11() throws XPathExpressionException { - assertEquals(xpath.evaluate("1+1", document, STRING), "2"); + assertEquals("2", xpath.evaluate("1+1", document, STRING)); } /** @@ -234,11 +227,10 @@ public class XPathTest { * returnType) throws XPathExpressionException if expression is a empty * string "". * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = XPathExpressionException.class) - public void testCheckXPath12() throws XPathExpressionException { - xpath.evaluate("", document, STRING); + @Test + public void testCheckXPath12() { + assertThrows(XPathExpressionException.class, () -> xpath.evaluate("", document, STRING)); } /** @@ -246,11 +238,10 @@ public class XPathTest { * returnType) throws IllegalArgumentException if returnType is not one of * the types defined in XPathConstants. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testCheckXPath13() throws XPathExpressionException { - xpath.evaluate(EXPRESSION_NAME_A, document, TEST_QNAME); + @Test + public void testCheckXPath13() { + assertThrows(IllegalArgumentException.class, () -> xpath.evaluate(EXPRESSION_NAME_A, document, TEST_QNAME)); } /** @@ -261,7 +252,7 @@ public class XPathTest { */ @Test public void testCheckXPath14() throws XPathExpressionException { - assertEquals(xpath.evaluate(EXPRESSION_NAME_A, document, BOOLEAN), true); + assertEquals(true, xpath.evaluate(EXPRESSION_NAME_A, document, BOOLEAN)); } /** @@ -273,7 +264,7 @@ public class XPathTest { */ @Test public void testCheckXPath15() throws XPathExpressionException { - assertEquals(xpath.evaluate(EXPRESSION_NAME_B, document, BOOLEAN), false); + assertEquals(false, xpath.evaluate(EXPRESSION_NAME_B, document, BOOLEAN)); } /** @@ -284,7 +275,7 @@ public class XPathTest { */ @Test public void testCheckXPath16() throws XPathExpressionException { - assertEquals(xpath.evaluate(EXPRESSION_NAME_A, document, NUMBER), 6d); + assertEquals(6d, xpath.evaluate(EXPRESSION_NAME_A, document, NUMBER)); } @@ -296,7 +287,7 @@ public class XPathTest { */ @Test public void testCheckXPath17() throws XPathExpressionException { - assertEquals(((Attr)xpath.evaluate(EXPRESSION_NAME_A, document, NODE)).getValue(), "6"); + assertEquals("6", ((Attr) xpath.evaluate(EXPRESSION_NAME_A, document, NODE)).getValue()); } /** @@ -308,19 +299,18 @@ public class XPathTest { */ @Test public void testCheckXPath18() throws XPathExpressionException { - NodeList nodeList = (NodeList)xpath.evaluate(EXPRESSION_NAME_A, document, NODESET); - assertEquals(((Attr) nodeList.item(0)).getValue(), "6"); + NodeList nodeList = (NodeList) xpath.evaluate(EXPRESSION_NAME_A, document, NODESET); + assertEquals("6", ((Attr) nodeList.item(0)).getValue()); } /** * Test for XPath.evaluate(java.lang.String expression, java.lang.Object * item). If expression is null, should throw NPE. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPath19() throws XPathExpressionException { - xpath.evaluate(null, document); + @Test + public void testCheckXPath19() { + assertThrows(NullPointerException.class, () -> xpath.evaluate(null, document)); } /** @@ -332,30 +322,27 @@ public class XPathTest { */ @Test public void testCheckXPath20() throws XPathExpressionException { - assertEquals(xpath.evaluate("1+1", document), "2"); + assertEquals("2", xpath.evaluate("1+1", document)); } /** * XPath.evaluate(java.lang.String expression, java.lang.Object item) throws * NPE if InputSource is null. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPath21() throws XPathExpressionException { - xpath.evaluate(EXPRESSION_NAME_A, null); + @Test + public void testCheckXPath21() { + assertThrows(NullPointerException.class, () -> xpath.evaluate(EXPRESSION_NAME_A, null)); } /** * XPath.evaluate(java.lang.String expression, InputSource source) return * correct value by looking for Node. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath22() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is)), "6"); + assertEquals("6", xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is))); } } @@ -363,23 +350,20 @@ public class XPathTest { * XPath.evaluate(java.lang.String expression, InputSource source) throws * NPE if InputSource is null. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPath23() throws XPathExpressionException { - xpath.evaluate(EXPRESSION_NAME_A, null); + @Test + public void testCheckXPath23() { + assertThrows(NullPointerException.class, () -> xpath.evaluate(EXPRESSION_NAME_A, null)); } /** * XPath.evaluate(java.lang.String expression, InputSource source) throws * NPE if String expression is null. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPath24() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.evaluate(null, new InputSource(is)); + assertThrows(NullPointerException.class, () -> xpath.evaluate(null, new InputSource(is))); } } @@ -387,39 +371,33 @@ public class XPathTest { * Test for XPath.evaluate(java.lang.String expression, InputSource source). * If expression is junk characters, expression cannot be evaluated, should * throw XPathExpressionException. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = XPathExpressionException.class) + @Test public void testCheckXPath25() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.evaluate("-*&", new InputSource(is)); + assertThrows(XPathExpressionException.class, () -> xpath.evaluate("-*&", new InputSource(is))); } } /** * XPath.evaluate(java.lang.String expression, InputSource source) throws * XPathExpressionException if expression is blank " ". - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = XPathExpressionException.class) + @Test public void testCheckXPath26() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.evaluate(" ", new InputSource(is)); + assertThrows(XPathExpressionException.class, () -> xpath.evaluate(" ", new InputSource(is))); } } /** * XPath.evaluate(java.lang.String expression, InputSource source, QName * returnType) returns correct string value which return type is String. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath27() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), STRING), "6"); + assertEquals("6", xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), STRING)); } } @@ -427,62 +405,53 @@ public class XPathTest { * XPath.evaluate(java.lang.String expression, InputSource source, QName * returnType) throws NPE if source is null. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPath28() throws XPathExpressionException { - xpath.evaluate(EXPRESSION_NAME_A, null, STRING); + @Test + public void testCheckXPath28() { + assertThrows(NullPointerException.class, () -> xpath.evaluate(EXPRESSION_NAME_A, null, STRING)); } /** * XPath.evaluate(java.lang.String expression, InputSource source, QName * returnType) throws NPE if expression is null. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPath29() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.evaluate(null, new InputSource(is), STRING); + assertThrows(NullPointerException.class, () -> xpath.evaluate(null, new InputSource(is), STRING)); } } /** * XPath.evaluate(java.lang.String expression, InputSource source, * QName returnType) throws NPE if returnType is null. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPath30() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), null); + assertThrows(NullPointerException.class, () -> xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), null)); } } /** * XPath.evaluate(java.lang.String expression, InputSource source, QName * returnType) throws XPathExpressionException if expression is junk characters. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = XPathExpressionException.class) + @Test public void testCheckXPath31() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.evaluate("-*&", new InputSource(is), STRING); + assertThrows(XPathExpressionException.class, () -> xpath.evaluate("-*&", new InputSource(is), STRING)); } } /** * XPath.evaluate(java.lang.String expression, InputSource source, QName * returnType) throws XPathExpressionException if expression is blank " ". - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = XPathExpressionException.class) + @Test public void testCheckXPath32() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.evaluate(" ", new InputSource(is), STRING); + assertThrows(XPathExpressionException.class, () -> xpath.evaluate(" ", new InputSource(is), STRING)); } } @@ -490,84 +459,72 @@ public class XPathTest { * XPath.evaluate(java.lang.String expression, InputSource source, * QName returnType) throws IllegalArgumentException if returnType is not * one of the types defined in XPathConstants. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testCheckXPath33() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), TEST_QNAME); + assertThrows(IllegalArgumentException.class, () -> xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), TEST_QNAME)); } } /** * XPath.evaluate(java.lang.String expression, InputSource source, * QName returnType) return correct boolean value if return type is Boolean. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath34() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), - BOOLEAN), true); + assertEquals(true, xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), + BOOLEAN)); } } /** * XPath.evaluate(java.lang.String expression, InputSource source, * QName returnType) return correct boolean value if return type is Boolean. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath35() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.evaluate(EXPRESSION_NAME_B, new InputSource(is), - BOOLEAN), false); + assertEquals(false, xpath.evaluate(EXPRESSION_NAME_B, new InputSource(is), + BOOLEAN)); } } /** * XPath.evaluate(java.lang.String expression, InputSource source, * QName returnType) return correct number value if return type is Number. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath36() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), - NUMBER), 6d); + assertEquals(6d, xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), + NUMBER)); } } /** * XPath.evaluate(java.lang.String expression, InputSource source, * QName returnType) return correct string value if return type is Node. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath37() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(((Attr)xpath.evaluate(EXPRESSION_NAME_A, - new InputSource(is), NODE)).getValue(), "6"); + assertEquals("6", ((Attr) xpath.evaluate(EXPRESSION_NAME_A, + new InputSource(is), NODE)).getValue()); } } /** * Test for XPath.evaluate(java.lang.String expression, InputSource source, * QName returnType) which return type is NodeList. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath38() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - NodeList nodeList = (NodeList)xpath.evaluate(EXPRESSION_NAME_A, - new InputSource(is), NODESET); - assertEquals(((Attr) nodeList.item(0)).getValue(), "6"); + NodeList nodeList = (NodeList) xpath.evaluate(EXPRESSION_NAME_A, + new InputSource(is), NODESET); + assertEquals("6", ((Attr) nodeList.item(0)).getValue()); } } @@ -575,57 +532,49 @@ public class XPathTest { * Test for XPath.evaluate(java.lang.String expression, InputSource iSource, * QName returnType). If return type is Boolean, should return false as * expression is not successful in evaluating to any result. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath52() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.evaluate(EXPRESSION_NAME_B, new InputSource(is), - BOOLEAN), false); + assertEquals(false, xpath.evaluate(EXPRESSION_NAME_B, new InputSource(is), + BOOLEAN)); } } /** * XPath.evaluate(java.lang.String expression, InputSource iSource, QName * returnType) returns correct number value which return type is Number. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath53() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), - NUMBER), 6d); + assertEquals(6d, xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), + NUMBER)); } } /** * XPath.evaluate(java.lang.String expression, InputSource iSource, QName * returnType) returns a node value if returnType is Node. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath54() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(((Attr)xpath.evaluate(EXPRESSION_NAME_A, - new InputSource(is), NODE)).getValue(), "6"); + assertEquals("6", ((Attr) xpath.evaluate(EXPRESSION_NAME_A, + new InputSource(is), NODE)).getValue()); } } /** * XPath.evaluate(java.lang.String expression, InputSource iSource, QName * returnType) returns a node list if returnType is NodeList. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath55() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - NodeList nodeList = (NodeList)xpath.evaluate(EXPRESSION_NAME_A, - new InputSource(is), NODESET); - assertEquals(((Attr) nodeList.item(0)).getValue(), "6"); + NodeList nodeList = (NodeList) xpath.evaluate(EXPRESSION_NAME_A, + new InputSource(is), NODESET); + assertEquals("6", ((Attr) nodeList.item(0)).getValue()); } } @@ -647,18 +596,18 @@ public class XPathTest { */ @Test public void testCheckXPath57() { - MyNamespaceContext myNamespaceContext = new MyNamespaceContext(); - xpath.setNamespaceContext(myNamespaceContext); - assertEquals(xpath.getNamespaceContext(), myNamespaceContext); + MyNamespaceContext expectedNamespaceContext = new MyNamespaceContext(); + xpath.setNamespaceContext(expectedNamespaceContext); + assertEquals(expectedNamespaceContext, xpath.getNamespaceContext()); } /** * Test for XPath.setNamespaceContext(NamespaceContext nsContext) Establish * a namespace context. NullPointerException is thrown if nsContext is null. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPath58() { - xpath.setNamespaceContext(null); + assertThrows(NullPointerException.class, () -> xpath.setNamespaceContext(null)); } /** @@ -685,9 +634,9 @@ public class XPathTest { * Test for XPath.setXPathFunctionResolver(XPathFunctionResolver resolver). * set resolver as null, should throw NPE. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPath61() { - xpath.setXPathFunctionResolver(null); + assertThrows(NullPointerException.class, () -> xpath.setXPathFunctionResolver(null)); } /** @@ -714,9 +663,9 @@ public class XPathTest { * Test for XPath.setXPathVariableResolver(XPathVariableResolver resolver). * Set resolver as null, should throw NPE. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPath64() { - xpath.setXPathVariableResolver(null); + assertThrows(NullPointerException.class, () -> xpath.setXPathVariableResolver(null)); } /** diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/AbstractCharacterDataTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/AbstractCharacterDataTest.java index c1b3381cb98..b46829de8e1 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/AbstractCharacterDataTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/AbstractCharacterDataTest.java @@ -22,27 +22,26 @@ */ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; -import static org.w3c.dom.DOMException.INDEX_SIZE_ERR; -import static org.w3c.dom.ptests.DOMTestUtil.DOMEXCEPTION_EXPECTED; - -import java.io.IOException; - -import javax.xml.parsers.ParserConfigurationException; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.w3c.dom.CharacterData; import org.w3c.dom.DOMException; import org.xml.sax.SAXException; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import static org.w3c.dom.DOMException.INDEX_SIZE_ERR; +import static org.w3c.dom.ptests.DOMTestUtil.DOMEXCEPTION_EXPECTED; + /* * @summary common test for the CharacterData Interface */ public abstract class AbstractCharacterDataTest { - @DataProvider(name = "data-for-length") - public Object[][] getDataForTestLength() { + public static Object[][] getDataForTestLength() { return new Object[][] { { "", 0 }, { "test", 4 } }; @@ -52,11 +51,11 @@ public abstract class AbstractCharacterDataTest { * Verify getLength method works as the spec, for an empty string, should * return zero */ - @Test(dataProvider = "data-for-length") + @ParameterizedTest + @MethodSource("getDataForTestLength") public void testGetLength(String text, int length) throws Exception { CharacterData cd = createCharacterData(text); - assertEquals(cd.getLength(), length); - + assertEquals(length, cd.getLength()); } /* @@ -66,12 +65,11 @@ public abstract class AbstractCharacterDataTest { public void testAppendData() throws Exception { CharacterData cd = createCharacterData("DOM"); cd.appendData("2"); - assertEquals(cd.getData(), "DOM2"); + assertEquals("DOM2", cd.getData()); } - @DataProvider(name = "data-for-delete") - public Object[][] getDataForTestDelete() { + public static Object[][] getDataForTestDelete() { return new Object[][] { { "DOM", 2, 1, "DO" }, { "DOM", 0, 2, "M" }, @@ -81,15 +79,15 @@ public abstract class AbstractCharacterDataTest { /* * Verify deleteData method works as the spec. */ - @Test(dataProvider = "data-for-delete") + @ParameterizedTest + @MethodSource("getDataForTestDelete") public void testDeleteData(String text, int offset, int count, String result) throws Exception { CharacterData cd = createCharacterData(text); cd.deleteData(offset, count); assertEquals(cd.getData(), result); } - @DataProvider(name = "data-for-replace") - public Object[][] getDataForTestReplace() { + public static Object[][] getDataForTestReplace() { return new Object[][] { { "DOM", 0, 3, "SAX", "SAX" }, { "DOM", 1, 1, "AA", "DAAM" }, @@ -100,15 +98,15 @@ public abstract class AbstractCharacterDataTest { /* * Verify replaceData method works as the spec. */ - @Test(dataProvider = "data-for-replace") + @ParameterizedTest + @MethodSource("getDataForTestReplace") public void testReplaceData(String text, int offset, int count, String arg, String result) throws Exception { CharacterData cd = createCharacterData(text); cd.replaceData(offset, count, arg); assertEquals(cd.getData(), result); } - @DataProvider(name = "data-for-replace-neg") - public Object[][] getDataForTestReplaceNeg() { + public static Object[][] getDataForTestReplaceNeg() { return new Object[][] { { "DOM", -1, 3, "SAX" }, //offset if neg { "DOM", 0, -1, "SAX" }, //count is neg @@ -119,19 +117,19 @@ public abstract class AbstractCharacterDataTest { * Test for replaceData method: verifies that DOMException with * INDEX_SIZE_ERR is thrown if offset or count is out of the bound. */ - @Test(dataProvider = "data-for-replace-neg") + @ParameterizedTest + @MethodSource("getDataForTestReplaceNeg") public void testReplaceDataNeg(String text, int offset, int count, String arg) throws Exception { CharacterData cd = createCharacterData(text); try { cd.replaceData(offset, count, arg); fail(DOMEXCEPTION_EXPECTED); } catch (DOMException e) { - assertEquals(e.code, INDEX_SIZE_ERR); + assertEquals(INDEX_SIZE_ERR, e.code); } } - @DataProvider(name = "data-for-insert") - public Object[][] getDataForTestInsert() { + public static Object[][] getDataForTestInsert() { return new Object[][] { { "DOM", 0, "SAX", "SAXDOM" }, { "DOM", 3, "SAX", "DOMSAX" } }; @@ -140,15 +138,15 @@ public abstract class AbstractCharacterDataTest { /* * Verify insertData method works as the spec. */ - @Test(dataProvider = "data-for-insert") + @ParameterizedTest + @MethodSource("getDataForTestInsert") public void testInsertData(String text, int offset, String arg, String result) throws Exception { CharacterData cd = createCharacterData(text); cd.insertData(offset, arg); assertEquals(cd.getData(), result); } - @DataProvider(name = "data-for-insert-neg") - public Object[][] getDataForTestInsertNeg() { + public static Object[][] getDataForTestInsertNeg() { return new Object[][] { { "DOM", -1 }, //offset is neg { "DOM", 4 } };//offset is greater than length @@ -158,14 +156,15 @@ public abstract class AbstractCharacterDataTest { * Test for insertData method: verifies that DOMException with * INDEX_SIZE_ERR is thrown if offset is out of the bound. */ - @Test(dataProvider = "data-for-insert-neg") + @ParameterizedTest + @MethodSource("getDataForTestInsertNeg") public void testInsertDataNeg(String text, int offset) throws Exception { CharacterData cd = createCharacterData(text); try { cd.insertData(offset, "TEST"); fail(DOMEXCEPTION_EXPECTED); } catch (DOMException e) { - assertEquals(e.code, INDEX_SIZE_ERR); + assertEquals(INDEX_SIZE_ERR, e.code); } } @@ -176,11 +175,10 @@ public abstract class AbstractCharacterDataTest { public void testSetData() throws Exception { CharacterData cd = createCharacterData("DOM"); cd.setData("SAX"); - assertEquals(cd.getData(), "SAX"); + assertEquals("SAX", cd.getData()); } - @DataProvider(name = "data-for-substring") - public Object[][] getDataForTestSubstring() { + public static Object[][] getDataForTestSubstring() { return new Object[][] { { "DOM Level 2", 0, 3, "DOM" }, { "DOM", 0, 3, "DOM" }, @@ -190,15 +188,14 @@ public abstract class AbstractCharacterDataTest { /* * Verify substringData method works as the spec. */ - @Test(dataProvider = "data-for-substring") + @ParameterizedTest + @MethodSource("getDataForTestSubstring") public void testSubstringData(String text, int offset, int count, String result) throws Exception { CharacterData cd = createCharacterData(text); - String retStr = cd.substringData(offset, count); - assertEquals(retStr, result); + assertEquals(result, cd.substringData(offset, count)); } - @DataProvider(name = "data-for-substring-neg") - public Object[][] getDataForTestSubstringNeg() { + public static Object[][] getDataForTestSubstringNeg() { return new Object[][] { { "DOM Level 2", -1, 3 }, //offset is neg { "DOM", 0, -1 }, //count is neg @@ -209,14 +206,15 @@ public abstract class AbstractCharacterDataTest { * Test for substringData method: verifies that DOMException with * INDEX_SIZE_ERR is thrown if offset or count is out of the bound. */ - @Test(dataProvider = "data-for-substring-neg") + @ParameterizedTest + @MethodSource("getDataForTestSubstringNeg") public void testSubstringDataNeg(String text, int offset, int count) throws Exception { CharacterData cd = createCharacterData(text); try { cd.substringData(offset, count); fail(DOMEXCEPTION_EXPECTED); } catch (DOMException e) { - assertEquals(e.code, INDEX_SIZE_ERR); + assertEquals(INDEX_SIZE_ERR, e.code); } } @@ -225,5 +223,4 @@ public abstract class AbstractCharacterDataTest { * Return a concrete CharacterData instance. */ abstract protected CharacterData createCharacterData(String text) throws IOException, SAXException, ParserConfigurationException; - } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/AttrTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/AttrTest.java index bd7dfb10eab..08ba9b81055 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/AttrTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/AttrTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -23,23 +23,23 @@ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import static org.w3c.dom.ptests.DOMTestUtil.createDOM; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.w3c.dom.ptests.DOMTestUtil.createDOM; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.AttrTest + * @run junit/othervm org.w3c.dom.ptests.AttrTest * @summary Test for the Attr Interface */ public class AttrTest { @@ -51,12 +51,12 @@ public class AttrTest { Document document = createDOM("Attr01.xml"); //test a new created Attr Attr attr = document.createAttribute("newAttribute"); - assertEquals(attr.getName(), "newAttribute"); + assertEquals("newAttribute", attr.getName()); //test a Attr loaded from xml file Element elemNode = (Element) document.getElementsByTagName("book").item(1); Attr attr2 = (Attr) elemNode.getAttributes().item(0); - assertEquals(attr2.getName(), "category1"); + assertEquals("category1", attr2.getName()); } /* @@ -71,7 +71,7 @@ public class AttrTest { NamedNodeMap nnMap = elemNode.getAttributes(); for (int i = 0; i < nnMap.getLength(); i++) { Attr attr = (Attr) nnMap.item(i); - assertEquals(attr.getOwnerElement().getNodeName(), "book"); + assertEquals("book", attr.getOwnerElement().getNodeName()); } //test an Attr without owner node @@ -143,8 +143,7 @@ public class AttrTest { Document document = createDOM("Attr01.xml"); Attr attr = document.createAttribute("newAttribute"); attr.setValue("newVal"); - assertEquals(attr.getValue(), "newVal"); - + assertEquals("newVal", attr.getValue()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/CommentTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/CommentTest.java index c3cff46c714..2f0664a8139 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/CommentTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/CommentTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,25 +22,24 @@ */ package org.w3c.dom.ptests; -import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; - -import java.io.IOException; - -import javax.xml.parsers.ParserConfigurationException; - import org.w3c.dom.CharacterData; import org.w3c.dom.Document; import org.xml.sax.SAXException; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; + +import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; + /* * @test * @library /javax/xml/jaxp/libs /javax/xml/jaxp/functional - * @run testng/othervm org.w3c.dom.ptests.CommentTest + * @run junit/othervm org.w3c.dom.ptests.CommentTest * @summary Test for Comment implementation returned by Document.createComment(String) */ public class CommentTest extends AbstractCharacterDataTest { @Override - protected CharacterData createCharacterData(String text) throws IOException, SAXException, ParserConfigurationException { + protected CharacterData createCharacterData(String text) throws ParserConfigurationException { Document document = createNewDocument(); return document.createComment(text); } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DocumentTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DocumentTest.java index 3ebf28afaa7..b7b5c2669ea 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DocumentTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DocumentTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,34 +22,35 @@ */ package org.w3c.dom.ptests; -import static javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI; -import static javax.xml.XMLConstants.XML_NS_URI; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.fail; -import static org.w3c.dom.DOMException.NAMESPACE_ERR; -import static org.w3c.dom.ptests.DOMTestUtil.DOMEXCEPTION_EXPECTED; -import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; -import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.w3c.dom.Attr; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; +import static javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI; +import static javax.xml.XMLConstants.XML_NS_URI; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; +import static org.w3c.dom.DOMException.NAMESPACE_ERR; +import static org.w3c.dom.ptests.DOMTestUtil.DOMEXCEPTION_EXPECTED; +import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; +import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.DocumentTest + * @run junit/othervm org.w3c.dom.ptests.DocumentTest * @summary Test createAttributeNS, getElementsByTagNameNS and createElementNS method of Document */ public class DocumentTest { - @DataProvider(name = "invalid-nsuri") - public Object[][] getInvalidNamespaceURI() { + public static Object[][] getInvalidNamespaceURI() { return new Object[][] { { " ", "xml:novel" }, //blank { "hello", "xml:novel" }, //unqualified @@ -61,14 +62,14 @@ public class DocumentTest { * Test for createAttributeNS method: verifies that DOMException is thrown * if reserved prefixes are used with an arbitrary namespace name. */ - @Test(dataProvider = "invalid-nsuri", expectedExceptions = DOMException.class) + @ParameterizedTest + @MethodSource("getInvalidNamespaceURI") public void testCreateAttributeNSNeg(String namespaceURI, String name) throws Exception { Document document = createDOMWithNS("DocumentTest01.xml"); - document.createAttributeNS(namespaceURI, name); + assertThrows(DOMException.class, () -> document.createAttributeNS(namespaceURI, name)); } - @DataProvider(name = "valid-nsuri") - public Object[][] getValidNamespaceURI() { + public static Object[][] getValidNamespaceURI() { return new Object[][] { { XML_NS_URI, "xml:novel" }, { XMLNS_ATTRIBUTE_NS_URI, "xmlns:novel" }, @@ -79,16 +80,16 @@ public class DocumentTest { /* * Verify the Attr from createAttributeNS. */ - @Test(dataProvider = "valid-nsuri") + @ParameterizedTest + @MethodSource("getValidNamespaceURI") public void testCreateAttributeNS(String namespaceURI, String name) throws Exception { Document document = createDOMWithNS("DocumentTest01.xml"); Attr attr = document.createAttributeNS(namespaceURI, name); - assertEquals(attr.getNamespaceURI(), namespaceURI); - assertEquals(attr.getName(), name); + assertEquals(namespaceURI, attr.getNamespaceURI()); + assertEquals(name, attr.getName()); } - @DataProvider(name = "elementName") - public Object[][] getElementName() { + public static Object[][] getElementName() { return new Object[][] { { "author", 1 }, { "b:author", 0 } }; @@ -97,25 +98,27 @@ public class DocumentTest { /* * Verify the NodeList from getElementsByTagNameNS. */ - @Test(dataProvider = "elementName") - public void testGetElementsByTagNameNS(String localName, int number) throws Exception { + @ParameterizedTest + @MethodSource("getElementName") + public void testGetElementsByTagNameNS(String localName, int expectedLength) throws Exception { Document document = createDOMWithNS("DocumentTest01.xml"); NodeList nodeList = document.getElementsByTagNameNS("urn:BooksAreUs.org:BookInfo", localName); - assertEquals(nodeList.getLength(), number); + assertEquals(expectedLength, nodeList.getLength()); } /* * Test for createElementNS method: verifies that DOMException is thrown * if reserved prefixes are used with an arbitrary namespace name. */ - @Test(dataProvider = "invalid-nsuri") + @ParameterizedTest + @MethodSource("getInvalidNamespaceURI") public void testCreateElementNSNeg(String namespaceURI, String name) throws Exception { Document document = createDOMWithNS("DocumentTest01.xml"); try { document.createElementNS(namespaceURI, name); fail(DOMEXCEPTION_EXPECTED); } catch (DOMException e) { - assertEquals(e.code, NAMESPACE_ERR); + assertEquals(NAMESPACE_ERR, e.code); } } @@ -129,9 +132,9 @@ public class DocumentTest { final String localName = "novel"; Document document = createDOMWithNS("DocumentTest01.xml"); Element element = document.createElementNS(nsURI, name); - assertEquals(element.getNamespaceURI(), nsURI); - assertEquals(element.getNodeName(), name); - assertEquals(element.getLocalName(), localName); + assertEquals(nsURI, element.getNamespaceURI()); + assertEquals(name, element.getNodeName()); + assertEquals(localName, element.getLocalName()); } /* @@ -166,9 +169,9 @@ public class DocumentTest { /* * Test createElement with unqualified xml name. */ - @Test(expectedExceptions = DOMException.class) + @Test public void testCreateElementNeg() throws Exception { Document doc = createNewDocument(); - doc.createElement("!nc$%^*(!"); + assertThrows(DOMException.class, () -> doc.createElement("!nc$%^*(!")); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DocumentTypeTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DocumentTypeTest.java index b5265b825b0..448e358c97a 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DocumentTypeTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DocumentTypeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -23,18 +23,18 @@ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.w3c.dom.ptests.DOMTestUtil.createDOM; - -import org.testng.Assert; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.DocumentType; import org.w3c.dom.NamedNodeMap; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.w3c.dom.ptests.DOMTestUtil.createDOM; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.DocumentTypeTest + * @run junit/othervm org.w3c.dom.ptests.DocumentTypeTest * @summary Test DocumentType */ public class DocumentTypeTest { @@ -48,10 +48,10 @@ public class DocumentTypeTest { NamedNodeMap namedNodeMap = documentType.getEntities(); // should return both external and internal. Parameter entities are not // contained. Duplicates are discarded. - assertEquals(namedNodeMap.getLength(), 3); - assertEquals(namedNodeMap.item(0).getNodeName(), "author"); - assertEquals(namedNodeMap.item(1).getNodeName(), "test"); - assertEquals(namedNodeMap.item(2).getNodeName(), "writer"); + assertEquals(3, namedNodeMap.getLength()); + assertEquals("author", namedNodeMap.item(0).getNodeName()); + assertEquals("test", namedNodeMap.item(1).getNodeName()); + assertEquals("writer", namedNodeMap.item(2).getNodeName()); } /* @@ -61,12 +61,11 @@ public class DocumentTypeTest { public void testGetNotations() throws Exception { DocumentType documentType = createDOM("DocumentType03.xml").getDoctype(); NamedNodeMap nm = documentType.getNotations(); - assertEquals(nm.getLength(), 2); // should return 2 because the notation - // name is repeated and - // it considers only the first - // occurence - assertEquals(nm.item(0).getNodeName(), "gs"); - assertEquals(nm.item(1).getNodeName(), "name"); + // should return 2 because the notation name is repeated, + // and it considers only the first occurrence + assertEquals(2, nm.getLength()); + assertEquals("gs", nm.item(0).getNodeName()); + assertEquals("name", nm.item(1).getNodeName()); } /* @@ -75,7 +74,7 @@ public class DocumentTypeTest { @Test public void testGetName() throws Exception { DocumentType documentType = createDOM("DocumentType03.xml").getDoctype(); - assertEquals(documentType.getName(), "note"); + assertEquals("note", documentType.getName()); } /* @@ -84,8 +83,8 @@ public class DocumentTypeTest { @Test public void testGetSystemId() throws Exception { DocumentType documentType = createDOM("DocumentType05.xml").getDoctype(); - assertEquals(documentType.getSystemId(), "DocumentBuilderImpl02.dtd"); - Assert.assertNull(documentType.getPublicId()); + assertEquals("DocumentBuilderImpl02.dtd", documentType.getSystemId()); + assertNull(documentType.getPublicId()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DomImplementationTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DomImplementationTest.java index 8e8191aa2a3..251279ced4f 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DomImplementationTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DomImplementationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,21 +22,22 @@ */ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; - -import javax.xml.parsers.ParserConfigurationException; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.DocumentType; +import javax.xml.parsers.ParserConfigurationException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.DomImplementationTest + * @run junit/othervm org.w3c.dom.ptests.DomImplementationTest * @summary Test DomImplementation API */ public class DomImplementationTest { @@ -50,8 +51,8 @@ public class DomImplementationTest { final String name = "document:localName"; DOMImplementation domImpl = getDOMImplementation(); Document document = domImpl.createDocument(nsURI, name, null); - assertEquals(document.getDocumentElement().getNamespaceURI(), nsURI); - assertEquals(document.getDocumentElement().getNodeName(), name); + assertEquals(nsURI, document.getDocumentElement().getNamespaceURI()); + assertEquals(name, document.getDocumentElement().getNodeName()); } /* @@ -85,8 +86,7 @@ public class DomImplementationTest { verifyDocumentType(document.getDoctype(), name, publicId, systemId); } - @DataProvider(name = "feature-supported") - public Object[][] getFeatureSupportedList() throws ParserConfigurationException { + public static Object[][] getFeatureSupportedList() throws ParserConfigurationException { DOMImplementation impl = getDOMImplementation(); return new Object[][] { { impl, "XML", "2.0", true }, @@ -109,20 +109,21 @@ public class DomImplementationTest { /* * Verify DOMImplementation for feature supporting. */ - @Test(dataProvider = "feature-supported") + @ParameterizedTest + @MethodSource("getFeatureSupportedList") public void testHasFeature(DOMImplementation impl, String feature, String version, boolean isSupported) { - assertEquals(impl.hasFeature(feature,version), isSupported); + assertEquals(isSupported, impl.hasFeature(feature, version)); } - private DOMImplementation getDOMImplementation() throws ParserConfigurationException { + private static DOMImplementation getDOMImplementation() throws ParserConfigurationException { return createNewDocument().getImplementation(); } - private void verifyDocumentType(DocumentType documentType, String name, String publicId, String systemId) { - assertEquals(documentType.getPublicId(), publicId); - assertEquals(documentType.getSystemId(), systemId); - assertEquals(documentType.getName(), name); + private static void verifyDocumentType(DocumentType documentType, String name, String publicId, String systemId) { + assertEquals(publicId, documentType.getPublicId()); + assertEquals(systemId, documentType.getSystemId()); + assertEquals(name, documentType.getName()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/ElementTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/ElementTest.java index b538f7d1738..b861e086b3e 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/ElementTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/ElementTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,23 +22,9 @@ */ package org.w3c.dom.ptests; -import static javax.xml.XMLConstants.XML_NS_URI; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; -import static org.w3c.dom.DOMException.INUSE_ATTRIBUTE_ERR; -import static org.w3c.dom.ptests.DOMTestUtil.DOMEXCEPTION_EXPECTED; -import static org.w3c.dom.ptests.DOMTestUtil.createDOM; -import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; -import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; - -import java.io.StringReader; - -import javax.xml.parsers.DocumentBuilderFactory; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.w3c.dom.Attr; import org.w3c.dom.DOMException; import org.w3c.dom.Document; @@ -47,10 +33,25 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.StringReader; + +import static javax.xml.XMLConstants.XML_NS_URI; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.w3c.dom.DOMException.INUSE_ATTRIBUTE_ERR; +import static org.w3c.dom.ptests.DOMTestUtil.DOMEXCEPTION_EXPECTED; +import static org.w3c.dom.ptests.DOMTestUtil.createDOM; +import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; +import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.ElementTest + * @run junit/othervm org.w3c.dom.ptests.ElementTest * @summary Test for the methods of Element Interface */ public class ElementTest { @@ -59,7 +60,7 @@ public class ElementTest { Document document = createDOMWithNS("ElementSample01.xml"); Element elemNode = (Element) document.getElementsByTagName("book").item(0); String s = elemNode.getAttributeNS("urn:BooksAreUs.org:BookInfo", "category"); - assertEquals(s, "research"); + assertEquals("research", s); } @Test @@ -67,7 +68,7 @@ public class ElementTest { Document document = createDOMWithNS("ElementSample01.xml"); Element elemNode = (Element) document.getElementsByTagName("book").item(0); Attr attr = elemNode.getAttributeNodeNS("urn:BooksAreUs.org:BookInfo", "category"); - assertEquals(attr.getValue(), "research"); + assertEquals("research", attr.getValue()); } @@ -80,11 +81,11 @@ public class ElementTest { Document document = createDOMWithNS("ElementSample01.xml"); Element elemNode = (Element) document.getElementsByTagName("book").item(1); Attr attr = elemNode.getAttributeNode("category1"); - assertEquals(attr.getValue(), "research"); + assertEquals("research", attr.getValue()); - assertEquals(elemNode.getTagName(), "book"); + assertEquals("book", elemNode.getTagName()); elemNode.removeAttributeNode(attr); - assertEquals(elemNode.getAttribute("category1"), ""); + assertEquals("", elemNode.getAttribute("category1")); } /* @@ -112,8 +113,8 @@ public class ElementTest { elemNode.normalize(); Node firstChild = elemNode.getFirstChild(); Node lastChild = elemNode.getLastChild(); - assertEquals(firstChild.getNodeValue(), "fjfjf"); - assertEquals(lastChild.getNodeValue(), "fjfjf"); + assertEquals("fjfjf", firstChild.getNodeValue()); + assertEquals("fjfjf", lastChild.getNodeValue()); } /* @@ -129,32 +130,32 @@ public class ElementTest { myAttr.setValue(attrValue); assertNull(elemNode.setAttributeNode(myAttr)); - assertEquals(elemNode.getAttribute(attrName), attrValue); + assertEquals(attrValue, elemNode.getAttribute(attrName)); } - @DataProvider(name = "attribute") - public Object[][] getAttributeData() { + public static Object[][] getAttributeData() { return new Object[][] { { "thisisname", "thisisitsvalue" }, { "style", "font-Family" } }; } - @Test(dataProvider = "attribute") + @ParameterizedTest + @MethodSource("getAttributeData") public void testSetAttribute(String name, String value) throws Exception { Document document = createDOM("ElementSample02.xml"); Element elemNode = document.createElement("pricetag2"); elemNode.setAttribute(name, value); - assertEquals(elemNode.getAttribute(name), value); + assertEquals(value, elemNode.getAttribute(name)); } /* * Negative test for setAttribute, null is not a valid name. */ - @Test(expectedExceptions = DOMException.class) + @Test public void testSetAttributeNeg() throws Exception { Document document = createDOM("ElementSample02.xml"); Element elemNode = document.createElement("pricetag2"); - elemNode.setAttribute(null, null); + assertThrows(DOMException.class, () -> elemNode.setAttribute(null, null)); } /* @@ -182,7 +183,7 @@ public class ElementTest { element3.setAttributeNode(attr); fail(DOMEXCEPTION_EXPECTED); } catch (DOMException doe) { - assertEquals(doe.code, INUSE_ATTRIBUTE_ERR); + assertEquals(INUSE_ATTRIBUTE_ERR, doe.code); } } @@ -201,8 +202,7 @@ public class ElementTest { assertNull(nl.item(0)); } - @DataProvider(name = "nsattribute") - public Object[][] getNSAttributeData() { + public static Object[][] getNSAttributeData() { return new Object[][] { { "h:html", "html", "attrValue" }, { "b:style", "style", "attrValue" } }; @@ -211,14 +211,15 @@ public class ElementTest { /* * setAttributeNodeNS and verify it with getAttributeNS. */ - @Test(dataProvider = "nsattribute") + @ParameterizedTest + @MethodSource("getNSAttributeData") public void testSetAttributeNodeNS(String qualifiedName, String localName, String value) throws Exception { Document document = createDOM("ElementSample03.xml"); Element elemNode = document.createElement("pricetag2"); Attr myAttr = document.createAttributeNS(XML_NS_URI, qualifiedName); myAttr.setValue(value); assertNull(elemNode.setAttributeNodeNS(myAttr)); - assertEquals(elemNode.getAttributeNS(XML_NS_URI, localName), value); + assertEquals(value, elemNode.getAttributeNS(XML_NS_URI, localName)); } @Test @@ -233,22 +234,23 @@ public class ElementTest { @Test public void testToString() throws Exception { final String xml = - "" - + "" - + "" - + " \n" - + " " - + " " - + ""; + """ + \ + \ + \ + + \ + \ + """; Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(xml))); Element root = doc.getDocumentElement(); - assertEquals(root.toString(), "[datacenterlist: null]"); + assertEquals("[datacenterlist: null]", root.toString()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/EntityChildTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/EntityChildTest.java index 65fe0e046ab..447100f45c9 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/EntityChildTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/EntityChildTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,23 +22,22 @@ */ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.w3c.dom.ptests.DOMTestUtil.XML_DIR; - -import java.io.File; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.File; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.w3c.dom.ptests.DOMTestUtil.XML_DIR; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.EntityChildTest + * @run junit/othervm org.w3c.dom.ptests.EntityChildTest * @summary Test DOM Parser: parsing an xml file that contains external entities. */ public class EntityChildTest { @@ -54,7 +53,7 @@ public class EntityChildTest { Element root = document.getDocumentElement(); NodeList n = root.getElementsByTagName("table"); NodeList nl = n.item(0).getChildNodes(); - assertEquals(n.getLength(), 1); - assertEquals(nl.getLength(), 3); + assertEquals(1, n.getLength()); + assertEquals(3, nl.getLength()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NamedNodeMapTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NamedNodeMapTest.java index 86cd3aa7bbb..5cf6bc2387c 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NamedNodeMapTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NamedNodeMapTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,21 +22,21 @@ */ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; -import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.NamedNodeMapTest + * @run junit/othervm org.w3c.dom.ptests.NamedNodeMapTest * @summary Test for the methods of NamedNodeMap Interface */ public class NamedNodeMapTest { @@ -61,9 +61,9 @@ public class NamedNodeMapTest { // setting to a new Value attr.setValue("newValue"); Node replacedAttr = namedNodeMap.setNamedItemNS(attr); // return the replaced attr - assertEquals(replacedAttr.getNodeValue(), "font-family"); + assertEquals("font-family", replacedAttr.getNodeValue()); Node updatedAttr = namedNodeMap.getNamedItemNS(nsURI, "style"); - assertEquals(updatedAttr.getNodeValue(), "newValue"); + assertEquals("newValue", updatedAttr.getNodeValue()); // creating a non existing attribute node @@ -75,7 +75,7 @@ public class NamedNodeMapTest { // checking if the node could be accessed // using the getNamedItemNS method Node newAttr = namedNodeMap.getNamedItemNS(nsURI, "newNode"); - assertEquals(newAttr.getNodeValue(), "newValue"); + assertEquals("newValue", newAttr.getNodeValue()); } /* @@ -89,7 +89,7 @@ public class NamedNodeMapTest { Node n = nodeList.item(7); NamedNodeMap namedNodeMap = n.getAttributes(); Node node = namedNodeMap.getNamedItemNS("urn:BooksAreUs.org:BookInfo", "aaa"); - assertEquals(node.getNodeValue(), "value"); + assertEquals("value", node.getNodeValue()); } @@ -107,14 +107,14 @@ public class NamedNodeMapTest { NamedNodeMap namedNodeMap = n.getAttributes(); Attr attr = document.createAttribute("name"); Node replacedAttr = namedNodeMap.setNamedItem(attr); - assertEquals(replacedAttr.getNodeValue(), "attributeValue"); + assertEquals("attributeValue", replacedAttr.getNodeValue()); Node updatedAttrNode = namedNodeMap.getNamedItem("name"); - assertEquals(updatedAttrNode.getNodeValue(), ""); + assertEquals("", updatedAttrNode.getNodeValue()); Attr newAttr = document.createAttribute("nonExistingName"); assertNull(namedNodeMap.setNamedItem(newAttr)); Node newAttrNode = namedNodeMap.getNamedItem("nonExistingName"); - assertEquals(newAttrNode.getNodeValue(), ""); + assertEquals("", newAttrNode.getNodeValue()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NodeListTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NodeListTest.java index 740c28279f6..e04b24bd9ae 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NodeListTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NodeListTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,31 +22,31 @@ */ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.w3c.dom.ptests.DOMTestUtil.createDOM; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.w3c.dom.ptests.DOMTestUtil.createDOM; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.NodeListTest + * @run junit/othervm org.w3c.dom.ptests.NodeListTest * @summary Verifies a bug found in jaxp1.0.1 and 1.1FCS. After going out of * bound, the last element of a NodeList returns null. The bug has been fixed * in jaxp 1.1.1 build. */ public class NodeListTest { - @DataProvider(name = "xml") - public Object[][] getTestData() { + public static Object[][] getTestData() { return new Object[][] { { "nodelist.xml", "document" }, { "Node01.xml", "body" } }; } - @Test(dataProvider = "xml") + @ParameterizedTest + @MethodSource("getTestData") public void lastItemTest(String xmlFileName, String nodeName) throws Exception { Document document = createDOM(xmlFileName); @@ -56,8 +56,7 @@ public class NodeListTest { Element elem1 = (Element) nl.item(n - 1); nl.item(n); Element elem3 = (Element) nl.item(n - 1); - assertEquals(elem3, elem1); - + assertEquals(elem1, elem3); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NodeTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NodeTest.java index 8df0808f9d4..828503d0e0b 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NodeTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NodeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,29 +22,10 @@ */ package org.w3c.dom.ptests; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotEquals; -import static org.testng.Assert.assertTrue; -import static org.w3c.dom.ptests.DOMTestUtil.GOLDEN_DIR; -import static org.w3c.dom.ptests.DOMTestUtil.createDOM; -import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; -import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; - -import java.io.File; -import java.util.PropertyPermission; - -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.TransformerFactoryConfigurationError; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.DocumentFragment; @@ -52,15 +33,35 @@ import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.w3c.dom.ptests.DOMTestUtil.GOLDEN_DIR; +import static org.w3c.dom.ptests.DOMTestUtil.createDOM; +import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; +import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.NodeTest + * @run junit/othervm org.w3c.dom.ptests.NodeTest * @summary Test Node interface */ public class NodeTest { - @DataProvider(name = "feature-supported") - public Object[][] getFeatureSupportedList() throws Exception { + public static Object[][] getFeatureSupportedList() throws Exception { Document document = createDOMWithNS("Node01.xml"); Node node = document.getElementsByTagName("body").item(0); return new Object[][] { @@ -81,9 +82,10 @@ public class NodeTest { /* * Verify Node for feature supporting. */ - @Test(dataProvider = "feature-supported") + @ParameterizedTest + @MethodSource("getFeatureSupportedList") public void testHasFeature(Node node, String feature, String version, boolean supported) { - assertEquals(node.isSupported(feature, version), supported); + assertEquals(supported, node.isSupported(feature, version)); } /* @@ -98,7 +100,7 @@ public class NodeTest { Node node = document.getElementsByTagName("title").item(0); node.appendChild(document.createTextNode("test")); root.normalize(); - assertEquals(node.getChildNodes().item(0).getNodeValue(), "Typographytest"); + assertEquals("Typographytest", node.getChildNodes().item(0).getNodeValue()); } /* @@ -154,10 +156,10 @@ public class NodeTest { Element element = (Element) document.getElementsByTagName("sender").item(0); parentElement.insertBefore(createTestDocumentFragment(document), element); - String outputfile = USER_DIR + "InsertBefore.out"; + String outputfile = "InsertBefore.out"; String goldfile = GOLDEN_DIR + "InsertBeforeGF.out"; outputXml(document, outputfile); - assertTrue(compareWithGold(goldfile, outputfile)); + assertLinesMatch(goldfile, outputfile); } @@ -172,10 +174,10 @@ public class NodeTest { Element element = (Element) document.getElementsByTagName("sender").item(0); parentElement.replaceChild(createTestDocumentFragment(document), element); - String outputfile = USER_DIR + "ReplaceChild3.out"; + String outputfile = "ReplaceChild3.out"; String goldfile = GOLDEN_DIR + "ReplaceChild3GF.out"; outputXml(document, outputfile); - assertTrue(compareWithGold(goldfile, outputfile)); + assertLinesMatch(goldfile, outputfile); } /* @@ -183,14 +185,16 @@ public class NodeTest { * with a node which was created from a different document than the one * which is trying to use this method. It should throw a DOMException. */ - @Test(expectedExceptions = DOMException.class) + @Test public void testReplaceChildNeg() throws Exception { Document document = createDOM("Node04.xml"); Document doc2 = createNewDocument(); Element parentElement = (Element) document.getElementsByTagName("to").item(0); Element element = (Element) document.getElementsByTagName("sender").item(0); - parentElement.replaceChild(createTestDocumentFragment(doc2), element); + assertThrows( + DOMException.class, () -> + parentElement.replaceChild(createTestDocumentFragment(doc2), element)); } private DocumentFragment createTestDocumentFragment(Document document) { @@ -207,4 +211,10 @@ public class NodeTest { StreamResult streamResult = new StreamResult(new File(outputFileName)); transformer.transform(domSource, streamResult); } + + private static void assertLinesMatch(String goldenFile, String actual) throws IOException { + Assertions.assertLinesMatch( + Files.readAllLines(Path.of(goldenFile)), + Files.readAllLines(Path.of(actual))); + } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NotationTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NotationTest.java index 301df8e51c3..60630deb477 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NotationTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NotationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,23 +22,22 @@ */ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.w3c.dom.ptests.DOMTestUtil.createDOM; - -import java.io.IOException; - -import javax.xml.parsers.ParserConfigurationException; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Notation; import org.xml.sax.SAXException; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.w3c.dom.ptests.DOMTestUtil.createDOM; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.NotationTest + * @run junit/othervm org.w3c.dom.ptests.NotationTest * @summary Test for Notation interface */ public class NotationTest { @@ -47,7 +46,7 @@ public class NotationTest { */ @Test public void testGetSystemId() throws Exception { - assertEquals(findNotation("gs").getSystemId(), "http://who.knows.where/"); + assertEquals("http://who.knows.where/", findNotation("gs").getSystemId()); } /* @@ -55,7 +54,7 @@ public class NotationTest { */ @Test public void testGetPublicId() throws Exception { - assertEquals(findNotation("pubname").getPublicId(), "pubId"); + assertEquals("pubId", findNotation("pubname").getPublicId()); } //find notation in Notation01.xml diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/PITest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/PITest.java index ea6a9072002..0dbff540c97 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/PITest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/PITest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,17 +22,17 @@ */ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import org.w3c.dom.ProcessingInstruction; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.PITest + * @run junit/othervm org.w3c.dom.ptests.PITest * @summary Test for the methods of Processing Instruction */ public class PITest { @@ -43,11 +43,11 @@ public class PITest { public void test() throws Exception { Document document = createDOMWithNS("PITest01.xml"); ProcessingInstruction pi = document.createProcessingInstruction("PI", "processing"); - assertEquals(pi.getData(), "processing"); - assertEquals(pi.getTarget(), "PI"); + assertEquals("processing", pi.getData()); + assertEquals("PI", pi.getTarget()); pi.setData("newProcessing"); - assertEquals(pi.getData(), "newProcessing"); + assertEquals("newProcessing", pi.getData()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/TextTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/TextTest.java index e763eda5c35..507801d1a2d 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/TextTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/TextTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,15 +22,7 @@ */ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; -import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; - -import java.io.IOException; - -import javax.xml.parsers.ParserConfigurationException; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.CharacterData; import org.w3c.dom.Document; import org.w3c.dom.Node; @@ -38,10 +30,17 @@ import org.w3c.dom.NodeList; import org.w3c.dom.Text; import org.xml.sax.SAXException; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; +import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; + /* * @test * @library /javax/xml/jaxp/libs /javax/xml/jaxp/functional - * @run testng/othervm org.w3c.dom.ptests.TextTest + * @run junit/othervm org.w3c.dom.ptests.TextTest * @summary Test for Text implementation returned by Document.createTextNode(String) */ public class TextTest extends AbstractCharacterDataTest { @@ -60,8 +59,7 @@ public class TextTest extends AbstractCharacterDataTest { textNode.splitText(0); int increased = node.getChildNodes().getLength() - rawChildNum; - assertEquals(increased, 1); - + assertEquals(1, increased); } @Override diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/TypeInfoTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/TypeInfoTest.java index 1013d5f4baf..d101b45d5f1 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/TypeInfoTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/TypeInfoTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -22,24 +22,23 @@ */ package org.w3c.dom.ptests; -import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static org.testng.Assert.assertEquals; - -import java.io.StringReader; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.TypeInfo; import org.xml.sax.InputSource; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.StringReader; + +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static org.junit.jupiter.api.Assertions.assertEquals; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.TypeInfoTest + * @run junit/othervm org.w3c.dom.ptests.TypeInfoTest * @summary Test getTypeName and getTypeNamespace methods of TypeInfo interface */ public class TypeInfoTest { @@ -48,11 +47,14 @@ public class TypeInfoTest { */ @Test public void test() throws Exception { - TypeInfo typeInfo = getTypeOfRoot(SCHEMA_INSTANCE, "\n" + "\n"); - - assertEquals(typeInfo.getTypeName(), "Test"); - assertEquals(typeInfo.getTypeNamespace(), "testNS"); + TypeInfo typeInfo = getTypeOfRoot(SCHEMA_INSTANCE, + """ + + + """); + assertEquals("Test", typeInfo.getTypeName()); + assertEquals("testNS", typeInfo.getTypeNamespace()); } private TypeInfo getTypeOfRoot(String schemaText, String docText) throws Exception { @@ -87,53 +89,55 @@ public class TypeInfoTest { * Schema instance */ private static final String SCHEMA_INSTANCE = - "\n" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + "\n" - + " \n" - + "\n" - + " \n" - + "\n" - + " \n" - + " \n" - + " \n" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + "\n" - + "\n"; + """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """; } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttrImplTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttrImplTest.java index b43012a6197..2af17996158 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttrImplTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttrImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,19 +22,20 @@ */ package org.xml.sax.ptests; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.helpers.AttributesImpl; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + /** * Class containing the test cases for AttributesImpl API. */ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.AttrImplTest + * @run junit/othervm org.xml.sax.ptests.AttrImplTest */ public class AttrImplTest { private static final String CAR_URI = "http://www.cars.com/xml"; @@ -66,8 +67,8 @@ public class AttrImplTest { attr.addAttribute(CAR_URI, CAR_LOCALNAME, CAR_QNAME, CAR_TYPE, CAR_VALUE); attr.addAttribute(JEEP_URI, JEEP_LOCALNAME, JEEP_QNAME, JEEP_TYPE, JEEP_VALUE); - assertEquals(attr.getIndex(CAR_QNAME), 0); - assertEquals(attr.getIndex(JEEP_QNAME), 1); + assertEquals(0, attr.getIndex(CAR_QNAME)); + assertEquals(1, attr.getIndex(JEEP_QNAME)); } /** @@ -79,7 +80,7 @@ public class AttrImplTest { attr.addAttribute(CAR_URI, CAR_LOCALNAME, CAR_QNAME, CAR_TYPE, CAR_VALUE); attr.addAttribute(JEEP_URI, JEEP_LOCALNAME, JEEP_QNAME, JEEP_TYPE, JEEP_VALUE); - assertEquals(attr.getIndex(JEEP_URI, JEEP_LOCALNAME), 1); + assertEquals(1, attr.getIndex(JEEP_URI, JEEP_LOCALNAME)); } /** @@ -88,7 +89,7 @@ public class AttrImplTest { @Test public void testcase03() { AttributesImpl attr = new AttributesImpl(); - assertEquals(attr.getIndex(JEEP_URI, "whl"), -1); + assertEquals(-1, attr.getIndex(JEEP_URI, "whl")); } /** @@ -100,8 +101,8 @@ public class AttrImplTest { attr.addAttribute(CAR_URI, CAR_LOCALNAME, CAR_QNAME, CAR_TYPE, CAR_VALUE); attr.addAttribute(JEEP_URI, JEEP_LOCALNAME, JEEP_QNAME, JEEP_TYPE, JEEP_VALUE); - assertEquals(attr.getType(1), JEEP_TYPE); - assertEquals(attr.getType(JEEP_QNAME), JEEP_TYPE); + assertEquals(JEEP_TYPE, attr.getType(1)); + assertEquals(JEEP_TYPE, attr.getType(JEEP_QNAME)); } /** @@ -113,9 +114,9 @@ public class AttrImplTest { attr.addAttribute(CAR_URI, CAR_LOCALNAME, CAR_QNAME, CAR_TYPE, CAR_VALUE); attr.addAttribute(JEEP_URI, JEEP_LOCALNAME, JEEP_QNAME, JEEP_TYPE, JEEP_VALUE); - assertEquals(attr.getValue(1), JEEP_VALUE); - assertEquals(attr.getValue(attr.getQName(1)), JEEP_VALUE); - assertEquals(attr.getValue(attr.getURI(1), attr.getLocalName(1)), JEEP_VALUE); + assertEquals(JEEP_VALUE, attr.getValue(1)); + assertEquals(JEEP_VALUE, attr.getValue(attr.getQName(1))); + assertEquals(JEEP_VALUE, attr.getValue(attr.getURI(1), attr.getLocalName(1))); } /** @@ -129,11 +130,11 @@ public class AttrImplTest { attr.addAttribute(JEEP_URI, JEEP_LOCALNAME, JEEP_QNAME, JEEP_TYPE, JEEP_VALUE); attr.setAttribute(1, "www.megginson.com", "author", "meg", "s", "SAX2"); - assertEquals(attr.getLocalName(1), "author"); - assertEquals(attr.getQName(1), "meg"); - assertEquals(attr.getType(1), "s"); - assertEquals(attr.getType("meg"), "s"); - assertEquals(attr.getURI(1), "www.megginson.com"); + assertEquals("author", attr.getLocalName(1)); + assertEquals("meg", attr.getQName(1)); + assertEquals("s", attr.getType(1)); + assertEquals("s", attr.getType("meg")); + assertEquals("www.megginson.com", attr.getURI(1)); } /** @@ -152,11 +153,11 @@ public class AttrImplTest { attr.setValue(1, "SAX01"); attr.setURI(1, "www.megginson.com/sax/sax01"); - assertEquals(attr.getLocalName(1), "speclead"); - assertEquals(attr.getQName(1), "megi"); - assertEquals(attr.getType(1), "sax"); - assertEquals(attr.getType("megi"), "sax"); - assertEquals(attr.getURI(1), "www.megginson.com/sax/sax01"); + assertEquals("speclead", attr.getLocalName(1)); + assertEquals("megi", attr.getQName(1)); + assertEquals("sax", attr.getType(1)); + assertEquals("sax", attr.getType("megi")); + assertEquals("www.megginson.com/sax/sax01", attr.getURI(1)); } /** @@ -165,11 +166,11 @@ public class AttrImplTest { @Test public void testcase08() { AttributesImpl attr = new AttributesImpl(); - assertEquals(attr.getLength(), 0); + assertEquals(0, attr.getLength()); attr.addAttribute(CAR_URI, CAR_LOCALNAME, CAR_QNAME, CAR_TYPE, CAR_VALUE); attr.addAttribute(JEEP_URI, JEEP_LOCALNAME, JEEP_QNAME, JEEP_TYPE, JEEP_VALUE); - assertEquals(attr.getLength(), 2); + assertEquals(2, attr.getLength()); } /** @@ -189,13 +190,13 @@ public class AttrImplTest { * Javadoc says java.lang.ArrayIndexOutOfBoundsException is thrown When the * supplied index does not point to an attribute in the list. */ - @Test(expectedExceptions = ArrayIndexOutOfBoundsException.class) + @Test public void testcase10() { AttributesImpl attr = new AttributesImpl(); attr.addAttribute(CAR_URI, CAR_LOCALNAME, CAR_QNAME, CAR_TYPE, CAR_VALUE); attr.addAttribute(JEEP_URI, JEEP_LOCALNAME, JEEP_QNAME, JEEP_TYPE, JEEP_VALUE); attr.removeAttribute(1); - attr.removeAttribute(1); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> attr.removeAttribute(1)); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttributesNSTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttributesNSTest.java index 5e7db17c746..687ea92c9c9 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttributesNSTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttributesNSTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,18 +22,17 @@ */ package org.xml.sax.ptests; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.File; +import org.junit.jupiter.api.Test; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; /** * This tests the Attributes interface. Here the startElement() callback of @@ -45,7 +44,7 @@ import org.testng.annotations.Test; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.AttributesNSTest + * @run junit/othervm org.xml.sax.ptests.AttributesNSTest */ public class AttributesNSTest { /** @@ -55,7 +54,7 @@ public class AttributesNSTest { */ @Test public void testcase01() throws Exception { - String outputFile = USER_DIR + "AttributesNS.out"; + String outputFile = "AttributesNS.out"; String goldFile = GOLDEN_DIR + "AttributesNSGF.out"; String xmlFile = XML_DIR + "namespace1.xml"; SAXParserFactory spf = SAXParserFactory.newInstance(); @@ -68,6 +67,8 @@ public class AttributesNSTest { MyAttrCHandler myAttrCHandler = new MyAttrCHandler(outputFile); saxParser.parse(new File(xmlFile), myAttrCHandler); myAttrCHandler.flushAndClose(); - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttributesTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttributesTest.java index 6ebdf070060..e37d0b93e5d 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttributesTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttributesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,18 +22,17 @@ */ package org.xml.sax.ptests; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.File; +import org.junit.jupiter.api.Test; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; /** * This tests the Attributes interface. Here the startElement() callback of @@ -46,7 +45,7 @@ import org.testng.annotations.Test; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.AttributesTest + * @run junit/othervm org.xml.sax.ptests.AttributesTest */ public class AttributesTest { /** @@ -57,7 +56,7 @@ public class AttributesTest { */ @Test public void testcase01() throws Exception { - String outputFile = USER_DIR + "Attributes.out"; + String outputFile = "Attributes.out"; String goldFile = GOLDEN_DIR + "AttributesGF.out"; String xmlFile = XML_DIR + "family.xml"; @@ -70,6 +69,8 @@ public class AttributesTest { MyAttrCHandler myAttrCHandler = new MyAttrCHandler(outputFile); saxParser.parse(new File(xmlFile), myAttrCHandler); myAttrCHandler.flushAndClose(); - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ContentHandlerTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ContentHandlerTest.java index b450caba7aa..0c06cd27b7e 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ContentHandlerTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ContentHandlerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,20 +22,7 @@ */ package org.xml.sax.ptests; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.BufferedWriter; -import java.io.FileInputStream; -import java.io.FileWriter; -import java.io.IOException; - -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.Locator; @@ -43,6 +30,18 @@ import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLFilterImpl; +import javax.xml.parsers.SAXParserFactory; +import java.io.BufferedWriter; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; + /** * Class registers a content event handler to XMLReader. Content event handler * transverses XML and print all visited node when XMLreader parses XML. Test @@ -51,7 +50,7 @@ import org.xml.sax.helpers.XMLFilterImpl; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.ContentHandlerTest + * @run junit/othervm org.xml.sax.ptests.ContentHandlerTest */ public class ContentHandlerTest { /** @@ -61,7 +60,7 @@ public class ContentHandlerTest { */ @Test public void testcase01() throws Exception { - String outputFile = USER_DIR + "Content.out"; + String outputFile = "Content.out"; String goldFile = GOLDEN_DIR + "ContentGF.out"; String xmlFile = XML_DIR + "namespace1.xml"; @@ -73,7 +72,9 @@ public class ContentHandlerTest { xmlReader.setContentHandler(cHandler); xmlReader.parse(new InputSource(instream)); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } @@ -112,7 +113,6 @@ class MyContentHandler extends XMLFilterImpl implements AutoCloseable { /** * Write characters tag along with content of characters when meet * characters event. - * @throws IOException error happen when writing file. */ @Override public void characters(char[] ch, int start, int length) throws SAXException { @@ -123,7 +123,6 @@ class MyContentHandler extends XMLFilterImpl implements AutoCloseable { /** * Write endDocument tag then flush the content and close the file when meet * endDocument event. - * @throws IOException error happen when writing file or closing file. */ @Override public void endDocument() throws SAXException { @@ -139,7 +138,6 @@ class MyContentHandler extends XMLFilterImpl implements AutoCloseable { /** * Write endElement tag with namespaceURI, localName, qName to the file when * meet endElement event. - * @throws IOException error happen when writing file. */ @Override public void endElement(String namespaceURI,String localName,String qName) throws SAXException{ @@ -150,7 +148,6 @@ class MyContentHandler extends XMLFilterImpl implements AutoCloseable { /** * Write endPrefixMapping tag along with prefix to the file when meet * endPrefixMapping event. - * @throws IOException error happen when writing file. */ @Override public void endPrefixMapping(String prefix) throws SAXException { @@ -160,7 +157,6 @@ class MyContentHandler extends XMLFilterImpl implements AutoCloseable { /** * Write ignorableWhitespace tag along with white spaces when meet * ignorableWhitespace event. - * @throws IOException error happen when writing file. */ @Override public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { @@ -172,7 +168,6 @@ class MyContentHandler extends XMLFilterImpl implements AutoCloseable { /** * Write processingInstruction tag along with target name and target data * when meet processingInstruction event. - * @throws IOException error happen when writing file. */ @Override public void processingInstruction(String target, String data) throws SAXException { @@ -196,7 +191,6 @@ class MyContentHandler extends XMLFilterImpl implements AutoCloseable { /** * Write skippedEntity tag along with entity name when meet skippedEntity * event. - * @throws IOException error happen when writing file. */ @Override public void skippedEntity(String name) throws SAXException { @@ -205,7 +199,6 @@ class MyContentHandler extends XMLFilterImpl implements AutoCloseable { /** * Write startDocument tag when meet startDocument event. - * @throws IOException error happen when writing file. */ @Override public void startDocument() throws SAXException { @@ -215,7 +208,6 @@ class MyContentHandler extends XMLFilterImpl implements AutoCloseable { /** * Write startElement tag along with namespaceURI, localName, qName, number * of attributes and line number when meet startElement event. - * @throws IOException error happen when writing file. */ @Override public void startElement(String namespaceURI, String localName, @@ -229,7 +221,6 @@ class MyContentHandler extends XMLFilterImpl implements AutoCloseable { /** * Write startPrefixMapping tag along with prefix and uri when meet * startPrefixMapping event. - * @throws IOException error happen when writing file. */ @Override public void startPrefixMapping(String prefix, String uri) throws SAXException { diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/DefaultHandlerTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/DefaultHandlerTest.java index f65a290cb2c..548cb6b4e43 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/DefaultHandlerTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/DefaultHandlerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,27 +22,26 @@ */ package org.xml.sax.ptests; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; - -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.helpers.DefaultHandler; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; + /** * XMLReader parse XML with default handler that transverses XML and * print all visited node. Test verifies output is same as the golden file. @@ -50,7 +49,7 @@ import org.xml.sax.helpers.DefaultHandler; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.DefaultHandlerTest + * @run junit/othervm org.xml.sax.ptests.DefaultHandlerTest */ public class DefaultHandlerTest { /** @@ -60,7 +59,7 @@ public class DefaultHandlerTest { */ @Test public void testDefaultHandler() throws Exception { - String outputFile = USER_DIR + "DefaultHandler.out"; + String outputFile = "DefaultHandler.out"; String goldFile = GOLDEN_DIR + "DefaultHandlerGF.out"; String xmlFile = XML_DIR + "namespace1.xml"; @@ -75,9 +74,9 @@ public class DefaultHandlerTest { if (File.separatorChar == '\\') newAbsolutePath = Absolutepath.replace('\\', '/'); saxparser.parse("file:///" + newAbsolutePath, handler); - - assertTrue(compareWithGold(goldFile, outputFile)); - + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } @@ -108,7 +107,6 @@ class MyDefaultHandler extends DefaultHandler { /** * Write characters tag along with content of characters when meet * characters event. - * @throws IOException error happen when writing file. */ @Override public void characters(char[] ch, int start, int length) throws SAXException { @@ -118,7 +116,6 @@ class MyDefaultHandler extends DefaultHandler { /** * Write endDocument tag then flush the content and close the file when meet * endDocument event. - * @throws IOException error happen when writing file or closing file. */ @Override public void endDocument() throws SAXException { @@ -134,7 +131,6 @@ class MyDefaultHandler extends DefaultHandler { /** * Write endElement tag with namespaceURI, localName, qName to the file when * meet endElement event. - * @throws IOException error happen when writing file. */ @Override public void endElement(String namespaceURI,String localName,String qName) throws SAXException{ @@ -145,7 +141,6 @@ class MyDefaultHandler extends DefaultHandler { /** * Write endPrefixMapping tag along with prefix to the file when meet * endPrefixMapping event. - * @throws IOException error happen when writing file. */ @Override public void endPrefixMapping(String prefix) throws SAXException { @@ -155,7 +150,6 @@ class MyDefaultHandler extends DefaultHandler { /** * Write error tag along with exception to the file when meet recoverable * error event. - * @throws IOException error happen when writing file. */ @Override public void error(SAXParseException e) throws SAXException { @@ -165,7 +159,6 @@ class MyDefaultHandler extends DefaultHandler { /** * Write fatalError tag along with exception to the file when meet * unrecoverable error event. - * @throws IOException error happen when writing file. */ @Override public void fatalError(SAXParseException e) throws SAXException { @@ -174,7 +167,6 @@ class MyDefaultHandler extends DefaultHandler { /** * Write warning tag along with exception to the file when meet warning event. - * @throws IOException error happen when writing file. */ @Override public void warning(SAXParseException e) throws SAXException { @@ -184,7 +176,6 @@ class MyDefaultHandler extends DefaultHandler { /** * Write ignorableWhitespace tag along with white spaces when meet * ignorableWhitespace event. - * @throws IOException error happen when writing file. */ @Override public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { @@ -196,7 +187,6 @@ class MyDefaultHandler extends DefaultHandler { /** * Write processingInstruction tag along with target name and target data * when meet processingInstruction event. - * @throws IOException error happen when writing file. */ @Override public void processingInstruction(String target, String data) throws SAXException { @@ -216,7 +206,6 @@ class MyDefaultHandler extends DefaultHandler { /** * Write skippedEntity tag along with entity name when meet skippedEntity * event. - * @throws IOException error happen when writing file. */ @Override public void skippedEntity(String name) throws SAXException { @@ -225,7 +214,6 @@ class MyDefaultHandler extends DefaultHandler { /** * Write startDocument tag when meet startDocument event. - * @throws IOException error happen when writing file. */ @Override public void startDocument() throws SAXException { @@ -235,7 +223,6 @@ class MyDefaultHandler extends DefaultHandler { /** * Write startElement tag along with namespaceURI, localName, qName, number * of attributes and line number when meet startElement event. - * @throws IOException error happen when writing file. */ @Override public void startElement(String namespaceURI, String localName, @@ -248,7 +235,6 @@ class MyDefaultHandler extends DefaultHandler { /** * Write startPrefixMapping tag along with prefix and uri when meet * startPrefixMapping event. - * @throws IOException error happen when writing file. */ @Override public void startPrefixMapping(String prefix, String uri) throws SAXException { diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/EHFatalTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/EHFatalTest.java index 507e9d28384..578ca881e48 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/EHFatalTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/EHFatalTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,28 +22,27 @@ */ package org.xml.sax.ptests; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; -import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.BufferedWriter; -import java.io.FileInputStream; -import java.io.FileWriter; -import java.io.IOException; - -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLFilterImpl; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import java.io.BufferedWriter; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; + /** * ErrorHandler unit test. Set a ErrorHandle to XMLReader. Capture fatal error * events in ErrorHandler. @@ -51,34 +50,31 @@ import org.xml.sax.helpers.XMLFilterImpl; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.EHFatalTest + * @run junit/othervm org.xml.sax.ptests.EHFatalTest */ public class EHFatalTest { /** * Error Handler to capture all error events to output file. Verifies the * output file is same as golden file. - * - * @throws Exception If any errors occur. */ @Test public void testEHFatal() throws Exception { - String outputFile = USER_DIR + "EHFatal.out"; + String outputFile = "EHFatal.out"; String goldFile = GOLDEN_DIR + "EHFatalGF.out"; String xmlFile = XML_DIR + "invalid.xml"; - try(MyErrorHandler eHandler = new MyErrorHandler(outputFile); - FileInputStream instream = new FileInputStream(xmlFile)) { - SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser(); - XMLReader xmlReader = saxParser.getXMLReader(); + SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser(); + XMLReader xmlReader = saxParser.getXMLReader(); + try (MyErrorHandler eHandler = new MyErrorHandler(outputFile); + FileInputStream instream = new FileInputStream(xmlFile)) { xmlReader.setErrorHandler(eHandler); InputSource is = new InputSource(instream); - xmlReader.parse(is); - fail("Parse should throw SAXException"); - } catch (SAXException expected) { - // This is expected. + assertThrows(SAXException.class, () -> xmlReader.parse(is)); } // Need close the output file before we compare it with golden file. - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } @@ -109,7 +105,6 @@ class MyErrorHandler extends XMLFilterImpl implements AutoCloseable { /** * Write fatalError tag along with exception to the file when meet * unrecoverable error event. - * @throws IOException error happen when writing file. */ @Override public void fatalError(SAXParseException e) throws SAXException { diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/NSSupportTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/NSSupportTest.java index 4ee4bf510a8..988bc3c92c9 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/NSSupportTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/NSSupportTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,13 +22,14 @@ */ package org.xml.sax.ptests; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; +import org.junit.jupiter.api.Test; +import org.xml.sax.helpers.NamespaceSupport; import java.util.Enumeration; -import org.testng.annotations.Test; -import org.xml.sax.helpers.NamespaceSupport; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; /** * Unit test cases for NamespaceSupport API @@ -36,7 +37,7 @@ import org.xml.sax.helpers.NamespaceSupport; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.NSSupportTest + * @run junit/othervm org.xml.sax.ptests.NSSupportTest */ public class NSSupportTest { /** @@ -70,14 +71,14 @@ public class NSSupportTest { support.declarePrefix(EMPTY_PREFIX, W3_URI); support.declarePrefix(DC_PREFIX, PURL_URI); - Enumeration e = support.getDeclaredPrefixes(); + Enumeration e = support.getDeclaredPrefixes(); int i = 0; - while(e.hasMoreElements()) { - prefixes[i++] = e.nextElement().toString(); + while (e.hasMoreElements()) { + prefixes[i++] = e.nextElement(); } support.popContext(); - assertEquals(prefixes, new String[]{EMPTY_PREFIX, DC_PREFIX}); + assertArrayEquals(new String[] { EMPTY_PREFIX, DC_PREFIX }, prefixes); } /** @@ -92,7 +93,7 @@ public class NSSupportTest { support.declarePrefix(DC_PREFIX, PURL_URI); parts = support.processName("dc:title", parts, false); support.popContext(); - assertEquals(parts, new String[]{PURL_URI, "title", "dc:title"}); + assertArrayEquals(new String[] { PURL_URI, "title", "dc:title" }, parts); } /** @@ -106,7 +107,7 @@ public class NSSupportTest { support.declarePrefix(EMPTY_PREFIX, W3_URI); parts = support.processName("a", parts, false); support.popContext(); - assertEquals(parts, new String[]{W3_URI, "a", "a"}); + assertArrayEquals(new String[] { W3_URI, "a", "a" }, parts); } @@ -121,8 +122,8 @@ public class NSSupportTest { support.declarePrefix(EMPTY_PREFIX, W3_URI); support.declarePrefix(DC_PREFIX, PURL_URI); - assertEquals(support.getURI(EMPTY_PREFIX), W3_URI); - assertEquals(support.getURI(DC_PREFIX), PURL_URI); + assertEquals(W3_URI, support.getURI(EMPTY_PREFIX)); + assertEquals(PURL_URI, support.getURI(DC_PREFIX)); support.popContext(); assertNull(support.getURI(EMPTY_PREFIX)); assertNull(support.getURI(DC_PREFIX)); diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/NSTableTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/NSTableTest.java index 81da8f25b88..eda3dc89fed 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/NSTableTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/NSTableTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,14 +22,14 @@ */ package org.xml.sax.ptests; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; +import org.junit.jupiter.api.Test; +import org.xml.sax.XMLReader; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; -import org.testng.annotations.Test; -import org.xml.sax.XMLReader; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Class containing the test cases for Namespace Table defined at @@ -38,7 +38,7 @@ import org.xml.sax.XMLReader; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.NSTableTest + * @run junit/othervm org.xml.sax.ptests.NSTableTest */ public class NSTableTest { private static final String NAMESPACES = diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ParserAdapterTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ParserAdapterTest.java index b4a59180bac..4aaefefaa31 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ParserAdapterTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ParserAdapterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,16 +22,7 @@ */ package org.xml.sax.ptests; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.FileInputStream; - -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -41,6 +32,15 @@ import org.xml.sax.helpers.ParserAdapter; import org.xml.sax.helpers.XMLFilterImpl; import org.xml.sax.helpers.XMLReaderAdapter; +import javax.xml.parsers.SAXParserFactory; +import java.io.FileInputStream; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; + /** * Unit test cases for ParserAdapter API. By default the only features recognized @@ -49,7 +49,7 @@ import org.xml.sax.helpers.XMLReaderAdapter; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.ParserAdapterTest + * @run junit/othervm org.xml.sax.ptests.ParserAdapterTest */ public class ParserAdapterTest { /** @@ -177,9 +177,9 @@ public class ParserAdapterTest { * * @exception Exception If any errors occur. */ - @Test(expectedExceptions = SAXNotRecognizedException.class) + @Test public void getFeature03() throws Exception { - parserAdapter.getFeature("no-meaning-feature"); + assertThrows(SAXNotRecognizedException.class, () -> parserAdapter.getFeature("no-meaning-feature")); } /** @@ -228,31 +228,25 @@ public class ParserAdapterTest { /** * NPE expected when parsing a null object by ParserAdapter. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void parse01() throws Exception { - parserAdapter.parse((InputSource)null); + assertThrows(NullPointerException.class, () -> parserAdapter.parse((InputSource) null)); } /** * SAXException expected when parsing a wrong-formatter XML with ParserAdapter. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class) + @Test public void parse02() throws Exception { try(FileInputStream fis = new FileInputStream(XML_DIR + "invalid.xml")) { InputSource is = new InputSource(fis); - parserAdapter.parse(is); + assertThrows(SAXException.class, () -> parserAdapter.parse(is)); } } /** * Parse a well-formatter XML with ParserAdapter. - * - * @throws Exception If any errors occur. */ @Test public void parse03() throws Exception { diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ResolverTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ResolverTest.java index a7463edb53c..483e8b87c1d 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ResolverTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ResolverTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,13 +22,14 @@ */ package org.xml.sax.ptests; -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; +import org.junit.jupiter.api.Test; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLFilterImpl; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileWriter; @@ -36,16 +37,13 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.XMLFilterImpl; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; /** * Entity resolver should be invoked in XML parse. This test verifies parsing @@ -54,24 +52,22 @@ import org.xml.sax.helpers.XMLFilterImpl; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.ResolverTest + * @run junit/othervm org.xml.sax.ptests.ResolverTest */ -@Test public class ResolverTest { /** * Unit test for entityResolver setter. - * - * @throws Exception If any errors occur. */ + @Test public void testResolver() throws Exception { - String outputFile = USER_DIR + "EntityResolver.out"; + String outputFile = "EntityResolver.out"; String goldFile = GOLDEN_DIR + "EntityResolverGF.out"; String xmlFile = XML_DIR + "publish.xml"; Files.copy(Paths.get(XML_DIR + "publishers.dtd"), - Paths.get(USER_DIR + "publishers.dtd"), REPLACE_EXISTING); + Paths.get("publishers.dtd"), REPLACE_EXISTING); Files.copy(Paths.get(XML_DIR + "familytree.dtd"), - Paths.get(USER_DIR + "familytree.dtd"), REPLACE_EXISTING); + Paths.get("familytree.dtd"), REPLACE_EXISTING); try(FileInputStream instream = new FileInputStream(xmlFile); MyEntityResolver eResolver = new MyEntityResolver(outputFile)) { @@ -81,7 +77,9 @@ public class ResolverTest { InputSource is = new InputSource(instream); xmlReader.parse(is); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLFilterCBTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLFilterCBTest.java index 4090b6b0733..377cd02ac7d 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLFilterCBTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLFilterCBTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,20 +22,7 @@ */ package org.xml.sax.ptests; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.BufferedWriter; -import java.io.FileInputStream; -import java.io.FileWriter; -import java.io.IOException; - -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.Locator; @@ -44,6 +31,18 @@ import org.xml.sax.SAXParseException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLFilterImpl; +import javax.xml.parsers.SAXParserFactory; +import java.io.BufferedWriter; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; + /** * Set parent of XMLFilter to XMLReader. Parsing on XML file will invoke XMLFilter * to write to output file. Test verifies output is same as the golden file. @@ -51,17 +50,15 @@ import org.xml.sax.helpers.XMLFilterImpl; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.XMLFilterCBTest + * @run junit/othervm org.xml.sax.ptests.XMLFilterCBTest */ -@Test public class XMLFilterCBTest { /** * Test XMLFilter working with XML reader. - * - * @throws Exception If any errors occur. */ + @Test public void testXMLFilterCB() throws Exception { - String outputFile = USER_DIR + "XMLFilter.out"; + String outputFile = "XMLFilter.out"; String goldFile = GOLDEN_DIR + "XMLFilterGF.out"; String xmlFile = XML_DIR + "namespace1.xml"; @@ -74,7 +71,9 @@ public class XMLFilterCBTest { myXmlFilter.parse(new InputSource(fis)); } // Need close the output file before we compare it with golden file. - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } @@ -103,7 +102,6 @@ class MyXMLFilter extends XMLFilterImpl implements AutoCloseable { /** * Write characters tag along with content of characters when meet * characters event. - * @throws IOException error happen when writing file. */ @Override public void characters(char[] ch, int start, int length) throws SAXException { @@ -114,7 +112,6 @@ class MyXMLFilter extends XMLFilterImpl implements AutoCloseable { /** * Write endDocument tag then flush the content and close the file when meet * endDocument event. - * @throws IOException error happen when writing file or closing file. */ @Override public void endDocument() throws SAXException { @@ -130,7 +127,6 @@ class MyXMLFilter extends XMLFilterImpl implements AutoCloseable { /** * Write endElement tag with namespaceURI, localName, qName to the file when * meet endElement event. - * @throws IOException error happen when writing file. */ @Override public void endElement(String namespaceURI,String localName,String qName) @@ -142,7 +138,6 @@ class MyXMLFilter extends XMLFilterImpl implements AutoCloseable { /** * Write endPrefixMapping tag along with prefix to the file when meet * endPrefixMapping event. - * @throws IOException error happen when writing file. */ @Override public void endPrefixMapping(String prefix) throws SAXException { @@ -152,7 +147,6 @@ class MyXMLFilter extends XMLFilterImpl implements AutoCloseable { /** * Write error tag along with exception to the file when meet recoverable * error event. - * @throws IOException error happen when writing file. */ @Override public void error(SAXParseException e) throws SAXException { @@ -162,7 +156,6 @@ class MyXMLFilter extends XMLFilterImpl implements AutoCloseable { /** * Write fatalError tag along with exception to the file when meet * unrecoverable error event. - * @throws IOException error happen when writing file. */ @Override public void fatalError(SAXParseException e) throws SAXException { @@ -171,7 +164,6 @@ class MyXMLFilter extends XMLFilterImpl implements AutoCloseable { /** * Write warning tag along with exception to the file when meet warning event. - * @throws IOException error happen when writing file. */ @Override public void warning(SAXParseException e) throws SAXException { @@ -181,7 +173,6 @@ class MyXMLFilter extends XMLFilterImpl implements AutoCloseable { /** * Write ignorableWhitespace tag along with white spaces when meet * ignorableWhitespace event. - * @throws IOException error happen when writing file. */ @Override public void ignorableWhitespace(char[] ch, int start, int length) @@ -194,7 +185,6 @@ class MyXMLFilter extends XMLFilterImpl implements AutoCloseable { /** * Write processingInstruction tag along with target name and target data * when meet processingInstruction event. - * @throws IOException error happen when writing file. */ @Override public void processingInstruction(String target, String data) @@ -218,7 +208,6 @@ class MyXMLFilter extends XMLFilterImpl implements AutoCloseable { /** * Write skippedEntity tag along with entity name when meet skippedEntity * event. - * @throws IOException error happen when writing file. */ @Override public void skippedEntity(String name) throws SAXException { @@ -227,7 +216,6 @@ class MyXMLFilter extends XMLFilterImpl implements AutoCloseable { /** * Write startDocument tag when meet startDocument event. - * @throws IOException error happen when writing file. */ @Override public void startDocument() throws SAXException { @@ -237,7 +225,6 @@ class MyXMLFilter extends XMLFilterImpl implements AutoCloseable { /** * Write startElement tag along with namespaceURI, localName, qName, number * of attributes and line number when meet startElement event. - * @throws IOException error happen when writing file. */ @Override public void startElement(String namespaceURI, String localName, @@ -250,7 +237,6 @@ class MyXMLFilter extends XMLFilterImpl implements AutoCloseable { /** * Write startPrefixMapping tag along with prefix and uri when meet * startPrefixMapping event. - * @throws IOException error happen when writing file. */ @Override public void startPrefixMapping(String prefix, String uri) throws SAXException { diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLFilterTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLFilterTest.java index 7ba882a9d1d..ee62b896b33 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLFilterTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLFilterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,28 +22,28 @@ */ package org.xml.sax.ptests; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.FileInputStream; - -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.InputSource; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLFilterImpl; +import javax.xml.parsers.SAXParserFactory; +import java.io.FileInputStream; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; + /** * Unit test for XMLFilter. */ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.XMLFilterTest + * @run junit/othervm org.xml.sax.ptests.XMLFilterTest */ public class XMLFilterTest { /** @@ -132,8 +132,6 @@ public class XMLFilterTest { /** * By default true is expected get namespaces feature. - * - * @throws Exception If any errors occur. */ @Test public void getFeature01() throws Exception { @@ -148,8 +146,6 @@ public class XMLFilterTest { /** * By default false is expected get namespaces-prefix feature. - * - * @throws Exception If any errors occur. */ @Test public void getFeature02() throws Exception { @@ -163,19 +159,15 @@ public class XMLFilterTest { /** * SAXNotRecognizedException is expected when get a feature by an invalid * feature name. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXNotRecognizedException.class) + @Test public void getFeature03() throws Exception { - new XMLFilterImpl().getFeature("no-meaning-feature"); + assertThrows(SAXNotRecognizedException.class, () -> new XMLFilterImpl().getFeature("no-meaning-feature")); } /** * Set namespaces feature to a value to XMLFilter. it's expected same when * obtain it again. - * - * @throws Exception If any errors occur. */ @Test public void setFeature01() throws Exception { @@ -193,8 +185,6 @@ public class XMLFilterTest { /** * Set namespaces-prefix feature to a value to XMLFilter. it's expected same * when obtain it again. - * - * @throws Exception If any errors occur. */ @Test public void setFeature02() throws Exception { @@ -211,35 +201,31 @@ public class XMLFilterTest { /** * NullPointerException is expected when parse a null InputSource. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void parse01() throws Exception { - new XMLFilterImpl().parse((InputSource)null); + assertThrows(NullPointerException.class, () -> new XMLFilterImpl().parse((InputSource) null)); } /** * SAXException is expected when parsing a invalid formatted XML file. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void parse02() throws Exception { - try(FileInputStream fis = new FileInputStream(XML_DIR + "invalid.xml")) { - new XMLFilterImpl().parse(new InputSource(fis)); + try (FileInputStream fis = new FileInputStream(XML_DIR + "invalid.xml")) { + InputSource input = new InputSource(fis); + assertThrows(NullPointerException.class, () -> new XMLFilterImpl().parse(input)); } } /** * No exception when parse a normal XML file. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void parse03() throws Exception { - try(FileInputStream fis = new FileInputStream(XML_DIR + "correct2.xml")) { - new XMLFilterImpl().parse(new InputSource(fis)); + try (FileInputStream fis = new FileInputStream(XML_DIR + "correct2.xml")) { + InputSource input = new InputSource(fis); + assertThrows(NullPointerException.class, () -> new XMLFilterImpl().parse(input)); } } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderAdapterTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderAdapterTest.java index 40462fcf076..65dbaba9971 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderAdapterTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderAdapterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,28 +22,27 @@ */ package org.xml.sax.ptests; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.FileInputStream; - -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.HandlerBase; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderAdapter; +import javax.xml.parsers.SAXParserFactory; +import java.io.FileInputStream; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; + /** * Class containing the test cases for XMLReaderAdapter API */ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.XMLReaderAdapterTest + * @run junit/othervm org.xml.sax.ptests.XMLReaderAdapterTest */ public class XMLReaderAdapterTest { /** @@ -64,21 +63,17 @@ public class XMLReaderAdapterTest { /** * To test the constructor that uses XMLReader. - * - * @throws Exception If any errors occur. */ @Test public void constructor02() throws Exception { XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader(); - assertNotNull(new XMLReaderAdapter(xmlReader)); + new XMLReaderAdapter(xmlReader); } /** * To test the parse method. The specification says that this method * will throw an exception if the embedded XMLReader does not support * the http://xml.org/sax/features/namespace-prefixes property. - * - * @throws Exception If any errors occur. */ @Test public void nsfeature01() throws Exception { @@ -93,8 +88,6 @@ public class XMLReaderAdapterTest { * To test the parse method. The specification says that this method * will throw an exception if the embedded XMLReader does not support * the http://xml.org/sax/features/namespace-prefixes property. - * - * @throws Exception If any errors occur. */ @Test public void parse01() throws Exception { diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderFactoryTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderFactoryTest.java index a109a27bce4..c8272d186ea 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderFactoryTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,21 +22,21 @@ */ package org.xml.sax.ptests; -import static jaxp.library.JAXPTestUtilities.setSystemProperty; - -import static org.testng.Assert.assertNotNull; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.SAXException; import org.xml.sax.helpers.XMLReaderFactory; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + /** * Unit test for XMLReaderFactory.createXMLReader API. */ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.XMLReaderFactoryTest + * @run junit/othervm org.xml.sax.ptests.XMLReaderFactoryTest */ public class XMLReaderFactoryTest { /** @@ -56,20 +56,25 @@ public class XMLReaderFactoryTest { */ @Test public void createReader02() throws SAXException { - setSystemProperty("org.xml.sax.driver", - "com.sun.org.apache.xerces.internal.parsers.SAXParser"); - assertNotNull(XMLReaderFactory. - createXMLReader("com.sun.org.apache.xerces.internal.parsers.SAXParser")); + System.setProperty("org.xml.sax.driver", + "com.sun.org.apache.xerces.internal.parsers.SAXParser"); + try { + assertNotNull(XMLReaderFactory.createXMLReader( + "com.sun.org.apache.xerces.internal.parsers.SAXParser")); + } finally { + System.clearProperty("org.xml.sax.driver"); + } } /** * SAXException expected when create XMLReader with an invalid driver name. * @throws org.xml.sax.SAXException expected Exception */ - @Test(expectedExceptions = SAXException.class, - expectedExceptionsMessageRegExp = - "SAX2 driver class org.apache.crimson.parser.ABCD not found") - public void createReader03() throws SAXException{ - XMLReaderFactory.createXMLReader("org.apache.crimson.parser.ABCD"); + @Test + public void createReader03() throws SAXException { + SAXException e = assertThrows( + SAXException.class, + () -> XMLReaderFactory.createXMLReader("org.apache.crimson.parser.ABCD")); + assertEquals("SAX2 driver class org.apache.crimson.parser.ABCD not found", e.getMessage()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderNSTableTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderNSTableTest.java index 9874866c7ec..2e7d0bc548f 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderNSTableTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderNSTableTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,20 +22,20 @@ */ package org.xml.sax.ptests; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.FileInputStream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.xml.sax.InputSource; +import org.xml.sax.XMLReader; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; -import org.testng.annotations.Test; -import org.xml.sax.InputSource; -import org.xml.sax.XMLReader; +import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; /** This class contains the testcases to test XMLReader with regard to * Namespace Table defined at @@ -44,9 +44,8 @@ import org.xml.sax.XMLReader; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.XMLReaderNSTableTest + * @run junit/othervm org.xml.sax.ptests.XMLReaderNSTableTest */ -@Test public class XMLReaderNSTableTest { /** * XML file that used to be parsed. @@ -62,11 +61,10 @@ public class XMLReaderNSTableTest { * namespace processing is enabled. namespace-prefix is also is enabled. * So it is a True-True combination. * The test is to test XMLReader with these conditions. - * - * @throws Exception If any errors occur. */ + @Test public void testWithTrueTrue() throws Exception { - String outputFile = USER_DIR + "XRNSTableTT.out"; + String outputFile = "XRNSTableTT.out"; String goldFile = GOLDEN_DIR + "NSTableTTGF.out"; SAXParserFactory spf = SAXParserFactory.newInstance(); @@ -79,18 +77,17 @@ public class XMLReaderNSTableTest { xmlReader.setContentHandler(handler); xmlReader.parse(new InputSource(fis)); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch(goldFile, outputFile); } /** * Namespace processing is enabled. Hence namespace-prefix is * expected to be automatically off. So it is a True-False combination. * The test is to test XMLReader with these conditions. - * - * @throws Exception If any errors occur. */ + @Test public void testWithTrueFalse() throws Exception { - String outputFile = USER_DIR + "XRNSTableTF.out"; + String outputFile = "XRNSTableTF.out"; String goldFile = GOLDEN_DIR + "NSTableTFGF.out"; SAXParserFactory spf = SAXParserFactory.newInstance(); @@ -103,18 +100,17 @@ public class XMLReaderNSTableTest { xmlReader.setContentHandler(handler); xmlReader.parse(new InputSource(fis)); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch(goldFile, outputFile); } /** * namespace processing is not enabled. Hence namespace-prefix is * expected to be automaically on. So it is a False-True combination. * The test is to test XMLReader with these conditions. - * - * @throws Exception If any errors occur. */ - public void testWithFalseTrue()throws Exception { - String outputFile = USER_DIR + "XRNSTableFT.out"; + @Test + public void testWithFalseTrue() throws Exception { + String outputFile = "XRNSTableFT.out"; String goldFile = GOLDEN_DIR + "NSTableFTGF.out"; SAXParserFactory spf = SAXParserFactory.newInstance(); @@ -125,6 +121,12 @@ public class XMLReaderNSTableTest { xmlReader.setContentHandler(handler); xmlReader.parse(new InputSource(fis)); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch(goldFile, outputFile); + } + + private static void assertLinesMatch(String goldenFile, String actual) throws IOException { + Assertions.assertLinesMatch( + Files.readAllLines(Path.of(goldenFile)), + Files.readAllLines(Path.of(actual))); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderTest.java index 497df48f7e4..f726e19922c 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,18 +22,7 @@ */ package org.xml.sax.ptests; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.FileInputStream; - -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; @@ -43,13 +32,24 @@ import org.xml.sax.ext.DeclHandler; import org.xml.sax.ext.LexicalHandler; import org.xml.sax.helpers.XMLFilterImpl; +import javax.xml.parsers.SAXParserFactory; +import java.io.FileInputStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; + /** * Class containing the test cases for SAXParser API */ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.XMLReaderTest + * @run junit/othervm org.xml.sax.ptests.XMLReaderTest */ public class XMLReaderTest { @@ -309,11 +309,11 @@ public class XMLReaderTest { * * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXNotRecognizedException.class) + @Test public void featureNE01() throws Exception { SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setNamespaceAware(true); - spf.newSAXParser().getXMLReader().getFeature("no-meaning-feature"); + assertThrows(SAXNotRecognizedException.class, () -> spf.newSAXParser().getXMLReader().getFeature("no-meaning-feature")); } /** @@ -328,7 +328,7 @@ public class XMLReaderTest { XMLReader xmlReader = spf.newSAXParser().getXMLReader(); XMLFilterImpl xmlFilter = new XMLFilterImpl(); xmlReader.setEntityResolver(xmlFilter); - assertEquals(xmlReader.getEntityResolver(), xmlFilter); + assertEquals(xmlFilter, xmlReader.getEntityResolver()); } /** @@ -355,7 +355,7 @@ public class XMLReaderTest { XMLReader xmlReader = spf.newSAXParser().getXMLReader(); XMLFilterImpl xmlFilter = new XMLFilterImpl(); xmlReader.setDTDHandler(xmlFilter); - assertEquals(xmlReader.getDTDHandler(), xmlFilter); + assertEquals(xmlFilter, xmlReader.getDTDHandler()); } /** @@ -382,7 +382,7 @@ public class XMLReaderTest { XMLReader xmlReader = spf.newSAXParser().getXMLReader(); XMLFilterImpl xmlFilter = new XMLFilterImpl(); xmlReader.setContentHandler(xmlFilter); - assertEquals(xmlReader.getContentHandler(), xmlFilter); + assertEquals(xmlFilter, xmlReader.getContentHandler()); } /** @@ -429,11 +429,11 @@ public class XMLReaderTest { * * @throws Exception If any errors occur. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void parse01() throws Exception { SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setNamespaceAware(true); - spf.newSAXParser().getXMLReader().parse((InputSource) null); + assertThrows(NullPointerException.class, () -> spf.newSAXParser().getXMLReader().parse((InputSource) null)); } /** @@ -441,12 +441,12 @@ public class XMLReaderTest { * * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class) + @Test public void parse02() throws Exception { try (FileInputStream fis = new FileInputStream(XML_DIR + "invalid.xml")) { SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setNamespaceAware(true); - spf.newSAXParser().getXMLReader().parse(new InputSource(fis)); + assertThrows(SAXException.class, () -> spf.newSAXParser().getXMLReader().parse(new InputSource(fis))); } } @@ -470,11 +470,11 @@ public class XMLReaderTest { * * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXNotSupportedException.class) + @Test public void xrProperty01() throws Exception { SAXParserFactory spf = SAXParserFactory.newInstance(); XMLReader xmlReader = spf.newSAXParser().getXMLReader(); - xmlReader.getProperty(XML_STRING); + assertThrows(SAXNotSupportedException.class, () -> xmlReader.getProperty(XML_STRING)); } /** @@ -483,11 +483,11 @@ public class XMLReaderTest { * * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXNotSupportedException.class) + @Test public void xrProperty02() throws Exception { SAXParserFactory spf = SAXParserFactory.newInstance(); XMLReader xmlReader = spf.newSAXParser().getXMLReader(); - assertNull(xmlReader.getProperty(DOM_NODE)); + assertThrows(SAXNotSupportedException.class, () -> xmlReader.getProperty(DOM_NODE)); } /** @@ -623,7 +623,7 @@ class MyDeclHandler implements DeclHandler { * @param eName The name of the associated element. * @param aName The name of the attribute. * @param type A string representing the attribute type. - * @param mode A string representing the attribute defaulting mode + * @param valueDefault A string representing the attribute defaulting mode * ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if none of these applies. * @param value A string representing the attribute's default value, or null * if there is none. diff --git a/test/jaxp/javax/xml/jaxp/functional/test/astro/AstroTest.java b/test/jaxp/javax/xml/jaxp/functional/test/astro/AstroTest.java index a42fcd9d3d7..ae7b88c5d34 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/astro/AstroTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/astro/AstroTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, 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 @@ -23,26 +23,25 @@ package test.astro; -import static java.lang.String.valueOf; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; -import static test.astro.AstroConstants.ASTROCAT; -import static test.astro.AstroConstants.GOLDEN_DIR; - -import java.nio.file.Files; -import java.nio.file.Paths; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import javax.xml.transform.sax.TransformerHandler; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import static java.lang.String.valueOf; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static test.astro.AstroConstants.ASTROCAT; +import static test.astro.AstroConstants.GOLDEN_DIR; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm test.astro.AstroTest + * @run junit/othervm test.astro.AstroTest * @summary run astro application, test xslt * * There are vast amounts of textual astronomical data, typically user is @@ -67,10 +66,11 @@ import org.testng.annotations.Test; * AstroProcessor to test different JAXP classes and features. * */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class AstroTest { private FiltersAndGolden[] data; - @BeforeClass + @BeforeAll public void setup() throws Exception { data = new FiltersAndGolden[4]; data[0] = new FiltersAndGolden(getGoldenFileName(1), astro -> astro.getRAFilter(0.106, 0.108)); @@ -82,8 +82,7 @@ public class AstroTest { /* * Provide permutations of InputSourceFactory and FilterFactory for test. */ - @DataProvider(name = "factories") - public Object[][] getQueryFactories() { + public static Object[][] getQueryFactories() { return new Object[][] { { StreamFilterFactoryImpl.class, InputSourceFactoryImpl.class }, { SAXFilterFactoryImpl.class, InputSourceFactoryImpl.class }, @@ -92,7 +91,8 @@ public class AstroTest { { StreamFilterFactoryImpl.class, DOML3InputSourceFactoryImpl.class } }; } - @Test(dataProvider = "factories") + @ParameterizedTest + @MethodSource("getQueryFactories") public void test(Class fFactClass, Class isFactClass) throws Exception { System.out.println(fFactClass.getName() +" : " + isFactClass.getName()); AstroProcessor astro = new AstroProcessor(fFactClass, ASTROCAT, isFactClass); @@ -108,10 +108,12 @@ public class AstroTest { for (int i = 0; i < filterCreators.length; i++) filters[i] = filterCreators[i].createFilter(astro); - String outputfile = Files.createTempFile(Paths.get(USER_DIR), "query" + processNum + ".out.", null).toString(); + String outputfile = Files.createTempFile(Paths.get("."), "query" + processNum + ".out.", null).toString(); System.out.println("output file: " + outputfile); astro.process(outputfile, filters); - assertTrue(compareWithGold(goldenFileName, outputfile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldenFileName)), + Files.readAllLines(Path.of(outputfile))); } private String getGoldenFileName(int num) { @@ -124,8 +126,8 @@ public class AstroTest { } private static class FiltersAndGolden { - private FilterCreator[] filters; - private String goldenFileName; + private final FilterCreator[] filters; + private final String goldenFileName; FiltersAndGolden(String goldenFileName, FilterCreator... filters) { this.filters = filters; diff --git a/test/jaxp/javax/xml/jaxp/functional/test/astro/DocumentLSTest.java b/test/jaxp/javax/xml/jaxp/functional/test/astro/DocumentLSTest.java index d337ee1bbad..4082e0603d6 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/astro/DocumentLSTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/astro/DocumentLSTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, 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 @@ -22,26 +22,7 @@ */ package test.astro; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.filenameToURL; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import static org.w3c.dom.ls.DOMImplementationLS.MODE_SYNCHRONOUS; -import static test.astro.AstroConstants.ASTROCAT; - -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Writer; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.ls.DOMImplementationLS; @@ -50,10 +31,27 @@ import org.w3c.dom.ls.LSOutput; import org.w3c.dom.ls.LSParser; import org.w3c.dom.ls.LSSerializer; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Writer; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.w3c.dom.ls.DOMImplementationLS.MODE_SYNCHRONOUS; +import static test.astro.AstroConstants.ASTROCAT; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm test.astro.DocumentLSTest + * @run junit/othervm test.astro.DocumentLSTest * @summary org.w3c.dom.ls tests */ public class DocumentLSTest { @@ -84,11 +82,11 @@ public class DocumentLSTest { assertTrue(src.getCertifiedText()); src.setCertifiedText(origCertified); // set back to orig - src.setSystemId(filenameToURL(ASTROCAT)); + src.setSystemId(Path.of(ASTROCAT).toUri().toASCIIString()); Document doc = domParser.parse(src); Element result = doc.getDocumentElement(); - assertEquals(result.getTagName(), "stardb"); + assertEquals("stardb", result.getTagName()); } } @@ -106,12 +104,12 @@ public class DocumentLSTest { domSerializer.getDomConfig().setParameter("xml-declaration", Boolean.FALSE); LSInput src = impl.createLSInput(); src.setStringData(xml); - assertEquals(src.getStringData(), xml); + assertEquals(xml, src.getStringData()); Document doc = domParser.parse(src); String result = domSerializer.writeToString(doc); - assertEquals(result, "runDocumentLS_Q6"); + assertEquals("runDocumentLS_Q6", result); } /* @@ -128,7 +126,7 @@ public class DocumentLSTest { impl = (DOMImplementationLS) db.getDOMImplementation(); LSSerializer domSerializer = impl.createLSSerializer(); MyDOMOutput mydomoutput = new MyDOMOutput(); - try (OutputStream os = new FileOutputStream(USER_DIR + "test.out")) { + try (OutputStream os = new FileOutputStream("test.out")) { mydomoutput.setByteStream(os); mydomoutput.setEncoding("UTF-8"); assertTrue(domSerializer.write(doc, mydomoutput)); diff --git a/test/jaxp/javax/xml/jaxp/functional/test/astro/NamespaceContextTest.java b/test/jaxp/javax/xml/jaxp/functional/test/astro/NamespaceContextTest.java index 75b7880a1ca..7e5000a440e 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/astro/NamespaceContextTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/astro/NamespaceContextTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, 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 @@ -22,18 +22,18 @@ */ package test.astro; -import static javax.xml.XMLConstants.DEFAULT_NS_PREFIX; -import static javax.xml.XMLConstants.NULL_NS_URI; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.api.Test; import javax.xml.namespace.QName; -import org.testng.annotations.Test; +import static javax.xml.XMLConstants.DEFAULT_NS_PREFIX; +import static javax.xml.XMLConstants.NULL_NS_URI; +import static org.junit.jupiter.api.Assertions.assertEquals; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm test.astro.NamespaceContextTest + * @run junit/othervm test.astro.NamespaceContextTest * @summary javax.xml.namespace.QName tests */ public class NamespaceContextTest { @@ -47,9 +47,9 @@ public class NamespaceContextTest { @Test public void testQNameConstructor() { QName qname = new QName(NS_URI, LOCAL_PART, PREFIX); - assertEquals(qname.getNamespaceURI(), NS_URI); - assertEquals(qname.getLocalPart(), LOCAL_PART); - assertEquals(qname.getPrefix(), PREFIX); + assertEquals(NS_URI, qname.getNamespaceURI()); + assertEquals(LOCAL_PART, qname.getLocalPart()); + assertEquals(PREFIX, qname.getPrefix()); } /* @@ -59,9 +59,9 @@ public class NamespaceContextTest { @Test public void testDefaultFields() { QName qname = new QName(LOCAL_PART); // just the local part specified - assertEquals(qname.getNamespaceURI(), NULL_NS_URI); - assertEquals(qname.getLocalPart(), LOCAL_PART); - assertEquals(qname.getPrefix(), DEFAULT_NS_PREFIX); + assertEquals(NULL_NS_URI, qname.getNamespaceURI()); + assertEquals(LOCAL_PART, qname.getLocalPart()); + assertEquals(DEFAULT_NS_PREFIX, qname.getPrefix()); } /* @@ -71,9 +71,9 @@ public class NamespaceContextTest { @Test public void testDefaultPrefix() { QName qname = new QName(NS_URI, LOCAL_PART); // no pref - assertEquals(qname.getNamespaceURI(), NS_URI); - assertEquals(qname.getLocalPart(), LOCAL_PART); - assertEquals(qname.getPrefix(), DEFAULT_NS_PREFIX); + assertEquals(NS_URI, qname.getNamespaceURI()); + assertEquals(LOCAL_PART, qname.getLocalPart()); + assertEquals(DEFAULT_NS_PREFIX, qname.getPrefix()); } /* @@ -83,6 +83,6 @@ public class NamespaceContextTest { @Test public void testQNameString() { QName qname = new QName(NS_URI, LOCAL_PART, PREFIX); - assertEquals(QName.valueOf(qname.toString()), qname); + assertEquals(qname, QName.valueOf(qname.toString())); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/test/astro/SAX201Test.java b/test/jaxp/javax/xml/jaxp/functional/test/astro/SAX201Test.java index 6cdbd93ce82..3384d04161a 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/astro/SAX201Test.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/astro/SAX201Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, 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 @@ -22,15 +22,15 @@ */ package test.astro; -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.XMLReader; +import javax.xml.parsers.SAXParserFactory; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm test.astro.SAX201Test + * @run junit/othervm test.astro.SAX201Test * @summary verify SAX 2.0.1 allows to use null in setters */ public class SAX201Test { diff --git a/test/jaxp/javax/xml/jaxp/functional/test/astro/SchemaValidationTest.java b/test/jaxp/javax/xml/jaxp/functional/test/astro/SchemaValidationTest.java index 782831e60b8..d2e5740f1f8 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/astro/SchemaValidationTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/astro/SchemaValidationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, 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 @@ -22,25 +22,25 @@ */ package test.astro; -import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static test.astro.AstroConstants.ASTROCAT; -import static test.astro.AstroConstants.JAXP_SCHEMA_LANGUAGE; -import static test.astro.AstroConstants.JAXP_SCHEMA_SOURCE; - -import java.io.File; +import org.junit.jupiter.api.Test; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +import java.io.File; -import org.testng.annotations.Test; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static test.astro.AstroConstants.ASTROCAT; +import static test.astro.AstroConstants.JAXP_SCHEMA_LANGUAGE; +import static test.astro.AstroConstants.JAXP_SCHEMA_SOURCE; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm test.astro.SchemaValidationTest + * @run junit/othervm test.astro.SchemaValidationTest * @summary test parser sets schema related properties to do validation */ public class SchemaValidationTest { @@ -59,11 +59,10 @@ public class SchemaValidationTest { * Test SAXException shall be thrown if schemaSource is set but * schemaLanguage is not set. */ - @Test(expectedExceptions = SAXException.class) + @Test public void testSchemaValidationNeg() throws Exception { SAXParser sp = getValidatingParser(); - sp.setProperty(JAXP_SCHEMA_SOURCE, "catalog.xsd"); - sp.parse(new File(ASTROCAT), new DefaultHandler()); + assertThrows(SAXException.class, () -> sp.setProperty(JAXP_SCHEMA_SOURCE, "catalog.xsd")); } private SAXParser getValidatingParser() throws ParserConfigurationException, SAXException { diff --git a/test/jaxp/javax/xml/jaxp/functional/test/astro/XPathAPITest.java b/test/jaxp/javax/xml/jaxp/functional/test/astro/XPathAPITest.java index 04bab01fa75..b305a060459 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/astro/XPathAPITest.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/astro/XPathAPITest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, 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 @@ -22,18 +22,17 @@ */ package test.astro; -import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static javax.xml.xpath.XPathConstants.DOM_OBJECT_MODEL; -import static javax.xml.xpath.XPathConstants.NODESET; -import static jaxp.library.JAXPTestUtilities.filenameToURL; -import static org.testng.Assert.assertEquals; -import static test.astro.AstroConstants.ASTROCAT; -import static test.astro.AstroConstants.JAXP_SCHEMA_LANGUAGE; -import static test.astro.AstroConstants.JAXP_SCHEMA_SOURCE; - -import java.net.MalformedURLException; -import java.util.ArrayList; -import java.util.Iterator; +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 org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; import javax.xml.namespace.NamespaceContext; import javax.xml.namespace.QName; @@ -44,21 +43,26 @@ import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import javax.xml.xpath.XPathVariableResolver; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Iterator; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static javax.xml.xpath.XPathConstants.DOM_OBJECT_MODEL; +import static javax.xml.xpath.XPathConstants.NODESET; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static test.astro.AstroConstants.ASTROCAT; +import static test.astro.AstroConstants.JAXP_SCHEMA_LANGUAGE; +import static test.astro.AstroConstants.JAXP_SCHEMA_SOURCE; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm test.astro.XPathAPITest + * @run junit/othervm test.astro.XPathAPITest * @summary test XPath API */ -@Test(singleThreaded = true) +@Execution(ExecutionMode.SAME_THREAD) +@TestInstance(Lifecycle.PER_CLASS) public class XPathAPITest { private static final String STARDB_STAR_3_CONSTELLATION = "//astro:stardb/astro:star[3]/astro:constellation"; private static final String STARDB_STAR = "//astro:stardb/astro:star"; @@ -66,7 +70,7 @@ public class XPathAPITest { private XPathFactory xpf; private NamespaceContext nsContext; - @BeforeClass + @BeforeAll public void setup() throws Exception { DocumentBuilderFactory df = DocumentBuilderFactory.newInstance(); df.setNamespaceAware(true); @@ -81,45 +85,49 @@ public class XPathAPITest { nsContext = new MyNamespaceContext(); } - @DataProvider(name = "nodelist-evaluator") - public Object[][] getNodeListEvaluator() throws MalformedURLException { - return new Object[][] { { (XPathEvaluator) expression -> getXPath().evaluate(expression, doc.getDocumentElement(), NODESET) }, - { (XPathEvaluator) expression -> getXPath().evaluate(expression, createXMLInputSource(), NODESET) }, - { (XPathEvaluator) expression -> getXPathExpression(expression).evaluate(doc.getDocumentElement(), NODESET) }, - { (XPathEvaluator) expression -> getXPathExpression(expression).evaluate(createXMLInputSource(), NODESET) } }; + public Object[] getNodeListEvaluator() { + return new Object[] { + (XPathEvaluator) expression -> getXPath().evaluate(expression, doc.getDocumentElement(), NODESET), + (XPathEvaluator) expression -> getXPath().evaluate(expression, createXMLInputSource(), NODESET), + (XPathEvaluator) expression -> getXPathExpression(expression).evaluate(doc.getDocumentElement(), NODESET), + (XPathEvaluator) expression -> getXPathExpression(expression).evaluate(createXMLInputSource(), NODESET), + }; } /* * Test xpath expression evaluations method that returns type indicated by * QName */ - @Test(dataProvider = "nodelist-evaluator") + @ParameterizedTest + @MethodSource("getNodeListEvaluator") public void testEvaluateNodeList(XPathEvaluator evaluator) throws Exception { NodeList o = (NodeList) evaluator.evaluate(STARDB_STAR); - assertEquals(o.getLength(), 10); + assertEquals(10, o.getLength()); } - @DataProvider(name = "string-evaluator") - public Object[][] getStringEvaluator() throws MalformedURLException { - return new Object[][] { { (XPathEvaluator) expression -> getXPath().evaluate(expression, doc.getDocumentElement()) }, - { (XPathEvaluator) expression -> getXPath().evaluate(expression, createXMLInputSource()) }, - { (XPathEvaluator) expression -> getXPathExpression(expression).evaluate(doc.getDocumentElement()) }, - { (XPathEvaluator) expression -> getXPathExpression(expression).evaluate(createXMLInputSource()) } }; + public Object[] getStringEvaluator() { + return new Object[] { + (XPathEvaluator) expression -> getXPath().evaluate(expression, doc.getDocumentElement()), + (XPathEvaluator) expression -> getXPath().evaluate(expression, createXMLInputSource()), + (XPathEvaluator) expression -> getXPathExpression(expression).evaluate(doc.getDocumentElement()), + (XPathEvaluator) expression -> getXPathExpression(expression).evaluate(createXMLInputSource()), + }; } /* * Test xpath expression evaluations method that returns String */ - @Test(dataProvider = "string-evaluator") + @ParameterizedTest + @MethodSource("getStringEvaluator") public void testEvaluateString(XPathEvaluator evaluator) throws Exception { - assertEquals(evaluator.evaluate(STARDB_STAR_3_CONSTELLATION), "Psc"); + assertEquals("Psc", evaluator.evaluate(STARDB_STAR_3_CONSTELLATION)); } @Test public void testXPathVariableResolver() throws Exception { XPath xpath = getXPath(); xpath.setXPathVariableResolver(new MyXPathVariableResolver()); - assertEquals(xpath.evaluate("//astro:stardb/astro:star[astro:hr=$id]/astro:constellation", doc.getDocumentElement()), "Peg"); + assertEquals("Peg", xpath.evaluate("//astro:stardb/astro:star[astro:hr=$id]/astro:constellation", doc.getDocumentElement())); } @@ -142,15 +150,15 @@ public class XPathAPITest { return "http://www.astro.com/astro".equals(nsURI) ? "astro" : ""; } - public Iterator getPrefixes(String nsURI) { - ArrayList list = new ArrayList(); + public Iterator getPrefixes(String nsURI) { + ArrayList list = new ArrayList<>(); list.add("astro"); return list.iterator(); } } @FunctionalInterface - private interface XPathEvaluator { + public interface XPathEvaluator { Object evaluate(String expression) throws XPathExpressionException; } @@ -165,6 +173,6 @@ public class XPathAPITest { } private InputSource createXMLInputSource() { - return new InputSource(filenameToURL(ASTROCAT)); + return new InputSource(Path.of(ASTROCAT).toUri().toASCIIString()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/AuctionController.java b/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/AuctionController.java index b2a66629e39..a3cc438f394 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/AuctionController.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/AuctionController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,22 +22,18 @@ */ package test.auctionportal; -import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static jaxp.library.JAXPTestUtilities.bomStream; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; -import static test.auctionportal.HiBidConstants.JAXP_SCHEMA_LANGUAGE; -import static test.auctionportal.HiBidConstants.JAXP_SCHEMA_SOURCE; -import static test.auctionportal.HiBidConstants.PORTAL_ACCOUNT_NS; -import static test.auctionportal.HiBidConstants.XML_DIR; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.math.BigInteger; -import java.nio.file.Paths; -import java.util.GregorianCalendar; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.w3c.dom.Attr; +import org.w3c.dom.DOMConfiguration; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.w3c.dom.TypeInfo; +import org.w3c.dom.bootstrap.DOMImplementationRegistry; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSSerializer; import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.DatatypeFactory; @@ -51,17 +47,28 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.GregorianCalendar; -import org.testng.annotations.Test; -import org.w3c.dom.Attr; -import org.w3c.dom.DOMConfiguration; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; -import org.w3c.dom.TypeInfo; -import org.w3c.dom.bootstrap.DOMImplementationRegistry; -import org.w3c.dom.ls.DOMImplementationLS; -import org.w3c.dom.ls.LSSerializer; +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static test.auctionportal.HiBidConstants.JAXP_SCHEMA_LANGUAGE; +import static test.auctionportal.HiBidConstants.JAXP_SCHEMA_SOURCE; +import static test.auctionportal.HiBidConstants.PORTAL_ACCOUNT_NS; +import static test.auctionportal.HiBidConstants.XML_DIR; /** * This is the user controller class for the Auction portal HiBid.com. @@ -69,14 +76,13 @@ import org.w3c.dom.ls.LSSerializer; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm test.auctionportal.AuctionController + * @run junit/othervm test.auctionportal.AuctionController */ public class AuctionController { /** * Check for DOMErrorHandler handling DOMError. Before fix of bug 4890927 * DOMConfiguration.setParameter("well-formed",true) throws an exception. * - * @throws Exception If any errors occur. */ @Test public void testCreateNewItem2Sell() throws Exception { @@ -99,7 +105,6 @@ public class AuctionController { * Check for DOMErrorHandler handling DOMError. Before fix of bug 4896132 * test throws DOM Level 1 node error. * - * @throws Exception If any errors occur. */ @Test public void testCreateNewItem2SellRetry() throws Exception { @@ -131,7 +136,6 @@ public class AuctionController { * Check if setting the attribute to be of type ID works. This will affect * the Attr.isID method according to the spec. * - * @throws Exception If any errors occur. */ @Test public void testCreateID() throws Exception { @@ -152,7 +156,6 @@ public class AuctionController { /** * Check the user data on the node. * - * @throws Exception If any errors occur. */ @Test public void testCheckingUserData() throws Exception { @@ -164,45 +167,44 @@ public class AuctionController { DocumentBuilder docBuilder = dbf.newDocumentBuilder(); Document document = docBuilder.parse(xmlFile); - Element account = (Element)document.getElementsByTagNameNS(PORTAL_ACCOUNT_NS, "Account").item(0); - assertEquals(account.getNodeName(), "acc:Account"); + Element account = (Element) document.getElementsByTagNameNS(PORTAL_ACCOUNT_NS, "Account").item(0); + assertEquals("acc:Account", account.getNodeName()); Element firstName = (Element) document.getElementsByTagNameNS(PORTAL_ACCOUNT_NS, "FirstName").item(0); - assertEquals(firstName.getNodeName(), "FirstName"); + assertEquals("FirstName", firstName.getNodeName()); Document doc1 = docBuilder.newDocument(); Element someName = doc1.createElement("newelem"); someName.setUserData("mykey", "dd", - (operation, key, data, src, dst) -> { - System.err.println("In UserDataHandler" + key); - System.out.println("In UserDataHandler"); - }); - Element impAccount = (Element)document.importNode(someName, true); - assertEquals(impAccount.getNodeName(), "newelem"); + (operation, key, data, src, dst) -> { + System.err.println("In UserDataHandler" + key); + System.out.println("In UserDataHandler"); + }); + Element impAccount = (Element) document.importNode(someName, true); + assertEquals("newelem", impAccount.getNodeName()); document.normalizeDocument(); String data = (someName.getUserData("mykey")).toString(); - assertEquals(data, "dd"); + assertEquals("dd", data); } /** * Check the UTF-16 XMLEncoding xml file. * - * @throws Exception If any errors occur. - * @see movies.xml + * @see movies-utf16.xml */ - @Test - public void testCheckingEncoding() throws Exception { - // Note since movies.xml is UTF-16 encoding. We're not using stanard XML - // file suffix. - String xmlFile = XML_DIR + "movies.xml.data"; + @ParameterizedTest + @EnumSource(value=ByteOrder.class) + public void testCheckingEncoding(ByteOrder byteOrder) throws Exception { + String xmlFile = XML_DIR + "movies-utf16.xml"; - try (InputStream source = bomStream("UTF-16", xmlFile)) { + // File is stored as UTF-8, but declares itself as UTF-16 for testing. + try (InputStream source = utf16Stream(xmlFile, byteOrder)) { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); Document document = dbf.newDocumentBuilder().parse(source); - assertEquals(document.getXmlEncoding(), "UTF-16"); - assertEquals(document.getXmlStandalone(), true); + assertEquals("UTF-16", document.getXmlEncoding()); + assertTrue(document.getXmlStandalone()); } } @@ -210,7 +212,6 @@ public class AuctionController { * Check validation API features. A schema which is including in Bug 4909119 * used to be testing for the functionalities. * - * @throws Exception If any errors occur. * @see userDetails.xsd */ @Test @@ -244,7 +245,6 @@ public class AuctionController { /** * Check grammar caching with imported schemas. * - * @throws Exception If any errors occur. * @see coins.xsd * @see coinsImportMe.xsd */ @@ -279,7 +279,6 @@ public class AuctionController { * parsing using the SAXParser. SCHEMA_SOURCE attribute is using for this * test. * - * @throws Exception If any errors occur. * @see coins.xsd * @see coinsImportMe.xsd */ @@ -304,7 +303,6 @@ public class AuctionController { /** * Check usage of javax.xml.datatype.Duration class. * - * @throws Exception If any errors occur. */ @Test public void testGetItemDuration() throws Exception { @@ -326,18 +324,17 @@ public class AuctionController { Duration sellDuration = DatatypeFactory.newInstance().newDuration(childList.item(0).getNodeValue()); assertFalse(sellDuration.isShorterThan(duration)); assertFalse(sellDuration.isLongerThan(duration)); - assertEquals(sellDuration.getField(DatatypeConstants.DAYS), BigInteger.valueOf(365)); - assertEquals(sellDuration.normalizeWith(new GregorianCalendar(1999, 2, 22)), duration); + assertEquals(BigInteger.valueOf(365), sellDuration.getField(DatatypeConstants.DAYS)); + assertEquals(duration, sellDuration.normalizeWith(new GregorianCalendar(1999, 2, 22))); Duration myDuration = sellDuration.add(duration); - assertEquals(myDuration.normalizeWith(new GregorianCalendar(2003, 2, 22)), - DatatypeFactory.newInstance().newDuration("P730D")); + assertEquals(DatatypeFactory.newInstance().newDuration("P730D"), + myDuration.normalizeWith(new GregorianCalendar(2003, 2, 22))); } /** * Check usage of TypeInfo interface introduced in DOM L3. * - * @throws Exception If any errors occur. */ @Test public void testGetTypeInfo() throws Exception { @@ -354,12 +351,36 @@ public class AuctionController { Document document = docBuilder.parse(xmlFile); Element userId = (Element)document.getElementsByTagNameNS(PORTAL_ACCOUNT_NS, "UserID").item(0); TypeInfo typeInfo = userId.getSchemaTypeInfo(); - assertTrue(typeInfo.getTypeName().equals("nonNegativeInteger")); - assertTrue(typeInfo.getTypeNamespace().equals(W3C_XML_SCHEMA_NS_URI)); + assertEquals("nonNegativeInteger", typeInfo.getTypeName()); + assertEquals(W3C_XML_SCHEMA_NS_URI, typeInfo.getTypeNamespace()); Element role = (Element)document.getElementsByTagNameNS(PORTAL_ACCOUNT_NS, "Role").item(0); TypeInfo roletypeInfo = role.getSchemaTypeInfo(); - assertTrue(roletypeInfo.getTypeName().equals("BuyOrSell")); - assertTrue(roletypeInfo.getTypeNamespace().equals(PORTAL_ACCOUNT_NS)); + assertEquals("BuyOrSell", roletypeInfo.getTypeName()); + assertEquals(PORTAL_ACCOUNT_NS, roletypeInfo.getTypeNamespace()); + } + + /** Convert file contents to a given character set with BOM marker. */ + public static InputStream utf16Stream(String file, ByteOrder byteOrder) + throws IOException { + Charset charset; + byte[] head; + switch (byteOrder) { + case BIG_ENDIAN: + charset = StandardCharsets.UTF_16BE; + head = new byte[] { (byte) 0xFE, (byte) 0xFF }; + break; + case LITTLE_ENDIAN: + charset = StandardCharsets.UTF_16LE; + head = new byte[] { (byte) 0xFF, (byte) 0xFE }; + break; + default: + throw new AssertionError("Unsupported byte order: " + byteOrder); + } + byte[] content = Files.readString(Paths.get(file)).getBytes(charset); + ByteBuffer bb = ByteBuffer.allocate(head.length + content.length); + bb.put(head); + bb.put(content); + return new ByteArrayInputStream(bb.array()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/AuctionItemRepository.java b/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/AuctionItemRepository.java index 854cb7d1ad4..dca34a73d82 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/AuctionItemRepository.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/AuctionItemRepository.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,14 +22,31 @@ */ package test.auctionportal; -import static jaxp.library.JAXPTestUtilities.setSystemProperty; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Paths; import static javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING; import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareDocumentWithGold; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static test.auctionportal.HiBidConstants.GOLDEN_DIR; import static test.auctionportal.HiBidConstants.JAXP_SCHEMA_LANGUAGE; import static test.auctionportal.HiBidConstants.JAXP_SCHEMA_SOURCE; @@ -37,30 +54,13 @@ import static test.auctionportal.HiBidConstants.SP_ENTITY_EXPANSION_LIMIT; import static test.auctionportal.HiBidConstants.SP_MAX_OCCUR_LIMIT; import static test.auctionportal.HiBidConstants.XML_DIR; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStream; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; - -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.xml.sax.SAXParseException; - /** * This is a test class for the Auction portal HiBid.com. */ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm test.auctionportal.AuctionItemRepository + * @run junit/othervm test.auctionportal.AuctionItemRepository */ public class AuctionItemRepository { /** @@ -79,7 +79,6 @@ public class AuctionItemRepository { * not. Previous system property was changed to jdk.xml.entityExpansionLimit * see http://docs.oracle.com/javase/tutorial/jaxp/limits/limits.html. * - * @throws Exception If any errors occur. */ @Test public void testEntityExpansionSAXPos() throws Exception { @@ -88,7 +87,7 @@ public class AuctionItemRepository { // implementation limits. factory.setFeature(FEATURE_SECURE_PROCESSING, true); // Set entityExpansionLimit as 2 should expect fatalError - setSystemProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(128000)); + System.setProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(128000)); SAXParser parser = factory.newSAXParser(); MyErrorHandler fatalHandler = new MyErrorHandler(); @@ -101,27 +100,25 @@ public class AuctionItemRepository { * not. Previous system property was changed to jdk.xml.entityExpansionLimit * see http://docs.oracle.com/javase/tutorial/jaxp/limits/limits.html. * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXParseException.class) + @Test public void testEntityExpansionSAXNeg() throws Exception { SAXParserFactory factory = SAXParserFactory.newInstance(); // Secure processing will limit XML processing to conform to // implementation limits. factory.setFeature(FEATURE_SECURE_PROCESSING, true); // Set entityExpansionLimit as 2 should expect SAXParseException. - setSystemProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(2)); + System.setProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(2)); SAXParser parser = factory.newSAXParser(); MyErrorHandler fatalHandler = new MyErrorHandler(); - parser.parse(new File(ENTITY_XML), fatalHandler); + assertThrows(SAXParseException.class, () -> parser.parse(new File(ENTITY_XML), fatalHandler)); } /** * Testing set MaxOccursLimit to 10000 in the secure processing enabled for * SAXParserFactory. * - * @throws Exception If any errors occur. */ @Test public void testMaxOccurLimitPos() throws Exception { @@ -130,7 +127,7 @@ public class AuctionItemRepository { SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setValidating(true); factory.setFeature(FEATURE_SECURE_PROCESSING, true); - setSystemProperty(SP_MAX_OCCUR_LIMIT, String.valueOf(10000)); + System.setProperty(SP_MAX_OCCUR_LIMIT, String.valueOf(10000)); SAXParser parser = factory.newSAXParser(); parser.setProperty(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA_NS_URI); parser.setProperty(JAXP_SCHEMA_SOURCE, new File(schema_file)); @@ -145,13 +142,12 @@ public class AuctionItemRepository { * Use a DocumentBuilder to create a DOM object and see if Secure Processing * feature affects the entity expansion. * - * @throws Exception If any errors occur. */ @Test public void testEntityExpansionDOMPos() throws Exception { DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance(); dfactory.setFeature(FEATURE_SECURE_PROCESSING, true); - setSystemProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(10000)); + System.setProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(10000)); DocumentBuilder dBuilder = dfactory.newDocumentBuilder(); MyErrorHandler eh = new MyErrorHandler(); dBuilder.setErrorHandler(eh); @@ -164,27 +160,25 @@ public class AuctionItemRepository { * Processing feature and entityExpansionLimit value affects output. * Negative test that when entityExpansionLimit is too small. * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXParseException.class) + @Test public void testEntityExpansionDOMNeg() throws Exception { DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance(); dfactory.setFeature(FEATURE_SECURE_PROCESSING, true); - setSystemProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(2)); + System.setProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(2)); DocumentBuilder dBuilder = dfactory.newDocumentBuilder(); MyErrorHandler eh = new MyErrorHandler(); dBuilder.setErrorHandler(eh); - dBuilder.parse(ENTITY_XML); + assertThrows(SAXParseException.class, () -> dBuilder.parse(ENTITY_XML)); } /** * Test xi:include with a SAXParserFactory. * - * @throws Exception If any errors occur. */ - @Test(groups = {"readWriteLocalFiles"}) + @Test public void testXIncludeSAXPos() throws Exception { - String resultFile = USER_DIR + "doc_xinclude.out"; + String resultFile = "doc_xinclude.out"; String goldFile = GOLDEN_DIR + "doc_xincludeGold.xml"; String xmlFile = XML_DIR + "doc_xinclude.xml"; @@ -203,11 +197,10 @@ public class AuctionItemRepository { * Test the simple case of including a document using xi:include using a * DocumentBuilder. * - * @throws Exception If any errors occur. */ - @Test(groups = {"readWriteLocalFiles"}) + @Test public void testXIncludeDOMPos() throws Exception { - String resultFile = USER_DIR + "doc_xincludeDOM.out"; + String resultFile = "doc_xincludeDOM.out"; String goldFile = GOLDEN_DIR + "doc_xincludeGold.xml"; String xmlFile = XML_DIR + "doc_xinclude.xml"; try (FileOutputStream fos = new FileOutputStream(resultFile)) { @@ -226,11 +219,10 @@ public class AuctionItemRepository { * Test the simple case of including a document using xi:include within a * xi:fallback using a DocumentBuilder. * - * @throws Exception If any errors occur. */ - @Test(groups = {"readWriteLocalFiles"}) + @Test public void testXIncludeFallbackDOMPos() throws Exception { - String resultFile = USER_DIR + "doc_fallbackDOM.out"; + String resultFile = "doc_fallbackDOM.out"; String goldFile = GOLDEN_DIR + "doc_fallbackGold.xml"; String xmlFile = XML_DIR + "doc_fallback.xml"; try (FileOutputStream fos = new FileOutputStream(resultFile)) { @@ -250,11 +242,10 @@ public class AuctionItemRepository { * Test for xi:fallback where the fall back text is parsed as text. This * test uses a nested xi:include for the fallback test. * - * @throws Exception If any errors occur. */ - @Test(groups = {"readWriteLocalFiles"}) + @Test public void testXIncludeFallbackTextPos() throws Exception { - String resultFile = USER_DIR + "doc_fallback_text.out"; + String resultFile = "doc_fallback_text.out"; String goldFile = GOLDEN_DIR + "doc_fallback_textGold.xml"; String xmlFile = XML_DIR + "doc_fallback_text.xml"; try (FileOutputStream fos = new FileOutputStream(resultFile)) { @@ -273,11 +264,10 @@ public class AuctionItemRepository { /** * Test the XPointer element() framework with XInclude. * - * @throws Exception If any errors occur. */ - @Test(groups = {"readWriteLocalFiles"}) + @Test public void testXpointerElementPos() throws Exception { - String resultFile = USER_DIR + "doc_xpointer_element.out"; + String resultFile = "doc_xpointer_element.out"; String goldFile = GOLDEN_DIR + "doc_xpointerGold.xml"; String xmlFile = XML_DIR + "doc_xpointer_element.xml"; try (FileOutputStream fos = new FileOutputStream(resultFile)) { @@ -297,11 +287,10 @@ public class AuctionItemRepository { /** * Test the XPointer framework with a SAX object. * - * @throws Exception If any errors occur. */ - @Test(groups = {"readWriteLocalFiles"}) + @Test public void testXPointerPos() throws Exception { - String resultFile = USER_DIR + "doc_xpointer.out"; + String resultFile = "doc_xpointer.out"; String goldFile = GOLDEN_DIR + "doc_xpointerGold.xml"; String xmlFile = XML_DIR + "doc_xpointer.xml"; @@ -320,11 +309,10 @@ public class AuctionItemRepository { * Test if xi:include may reference the doc containing the include if the * parse type is text. * - * @throws Exception If any errors occur. */ - @Test(groups = {"readWriteLocalFiles"}) + @Test public void testXIncludeLoopPos() throws Exception { - String resultFile = USER_DIR + "doc_xinc_loops.out"; + String resultFile = "doc_xinc_loops.out"; String goldFile = GOLDEN_DIR + "doc_xinc_loopGold.xml"; String xmlFile = XML_DIR + "doc_xinc_loops.xml"; @@ -347,11 +335,10 @@ public class AuctionItemRepository { * Test if two non nested xi:include elements can include the same document * with an xi:include statement. * - * @throws Exception If any errors occur. */ - @Test(groups = {"readWriteLocalFiles"}) + @Test public void testXIncludeNestedPos() throws Exception { - String resultFile = USER_DIR + "schedule.out"; + String resultFile = "schedule.out"; String goldFile = GOLDEN_DIR + "scheduleGold.xml"; String xmlFile = XML_DIR + "schedule.xml"; @@ -367,4 +354,20 @@ public class AuctionItemRepository { } assertTrue(compareDocumentWithGold(goldFile, resultFile)); } + + public static boolean compareDocumentWithGold(String goldfile, String resultFile) + throws ParserConfigurationException, SAXException, IOException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setCoalescing(true); + factory.setIgnoringElementContentWhitespace(true); + factory.setIgnoringComments(true); + DocumentBuilder db = factory.newDocumentBuilder(); + + Document goldD = db.parse(Paths.get(goldfile).toFile()); + goldD.normalizeDocument(); + Document resultD = db.parse(Paths.get(resultFile).toFile()); + resultD.normalizeDocument(); + return goldD.isEqualNode(resultD); + } } diff --git a/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/content/movies.xml.data b/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/content/movies-utf16.xml similarity index 100% rename from test/jaxp/javax/xml/jaxp/functional/test/auctionportal/content/movies.xml.data rename to test/jaxp/javax/xml/jaxp/functional/test/auctionportal/content/movies-utf16.xml diff --git a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4511326.java b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4511326.java index a8bac488b8e..a9bb95e20a8 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4511326.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4511326.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -23,19 +23,18 @@ package test.gaptest; -import java.io.StringReader; +import org.junit.jupiter.api.Test; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamSource; - -import org.testng.annotations.Test; +import java.io.StringReader; /* * @test * @bug 4511326 * @library /javax/xml/jaxp/libs - * @run testng/othervm test.gaptest.Bug4511326 + * @run junit/othervm test.gaptest.Bug4511326 * @summary In forwards-compatible mode the attribute isn't ignored */ public class Bug4511326 { diff --git a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4512806.java b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4512806.java index d27186bcf6d..2e7b05eaaac 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4512806.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4512806.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -23,24 +23,23 @@ package test.gaptest; -import static javax.xml.transform.OutputKeys.ENCODING; -import static javax.xml.transform.OutputKeys.INDENT; -import static org.testng.Assert.assertEquals; - -import java.io.StringReader; +import org.junit.jupiter.api.Test; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamSource; +import java.io.StringReader; -import org.testng.annotations.Test; +import static javax.xml.transform.OutputKeys.ENCODING; +import static javax.xml.transform.OutputKeys.INDENT; +import static org.junit.jupiter.api.Assertions.assertEquals; /* * @test * @bug 4512806 * @library /javax/xml/jaxp/libs - * @run testng/othervm test.gaptest.Bug4512806 + * @run junit/othervm test.gaptest.Bug4512806 * @summary test transformer.setOutputProperties(null) */ public class Bug4512806 { @@ -57,13 +56,13 @@ public class Bug4512806 { transformer.setOutputProperty(INDENT, "no"); transformer.setOutputProperty(ENCODING, "UTF-16"); - assertEquals(printPropertyValue(INDENT), "indent=no"); - assertEquals(printPropertyValue(ENCODING), "encoding=UTF-16"); + assertEquals("indent=no", printPropertyValue(INDENT)); + assertEquals("encoding=UTF-16", printPropertyValue(ENCODING)); transformer.setOutputProperties(null); - assertEquals(printPropertyValue(INDENT), "indent=yes"); - assertEquals(printPropertyValue(ENCODING), "encoding=UTF-8"); + assertEquals("indent=yes", printPropertyValue(INDENT)); + assertEquals("encoding=UTF-8", printPropertyValue(ENCODING)); } diff --git a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4515047.java b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4515047.java index 5fc68a28a6b..8466c9cff79 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4515047.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4515047.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -23,6 +23,8 @@ package test.gaptest; +import org.junit.jupiter.api.Test; + import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; @@ -31,13 +33,11 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; -import org.testng.annotations.Test; - /* * @test * @bug 4515047 * @library /javax/xml/jaxp/libs - * @run testng/othervm test.gaptest.Bug4515047 + * @run junit/othervm test.gaptest.Bug4515047 * @summary test transform an empty dom source */ public class Bug4515047 { diff --git a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4515660.java b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4515660.java index 014765f89d8..303db79f68b 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4515660.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4515660.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -23,14 +23,15 @@ package test.gaptest; -import static jaxp.library.JAXPTestUtilities.setSystemProperty; -import static jaxp.library.JAXPTestUtilities.clearSystemProperty; - -import static org.testng.Assert.assertTrue; - -import java.io.IOException; -import java.io.StringReader; -import java.io.StringWriter; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.XMLFilterImpl; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; @@ -41,32 +42,31 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.sax.SAXTransformerFactory; import javax.xml.transform.stream.StreamResult; +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.XMLFilterImpl; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @bug 4515660 * @library /javax/xml/jaxp/libs - * @run testng/othervm test.gaptest.Bug4515660 + * @run junit/othervm test.gaptest.Bug4515660 * @summary verify property org.xml.sax.driver is used by SAXTransformerFactory */ -@Test(singleThreaded = true) +@Execution(ExecutionMode.SAME_THREAD) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class Bug4515660 { - @BeforeClass + @BeforeAll public void setSaxDrier() { - setSystemProperty("org.xml.sax.driver", ReaderStub.class.getName()); + System.setProperty("org.xml.sax.driver", ReaderStub.class.getName()); } - @AfterClass + @AfterAll public void clearSaxDrier() { - clearSystemProperty("org.xml.sax.driver"); + System.clearProperty("org.xml.sax.driver"); } @Test @@ -88,9 +88,13 @@ public class Bug4515660 { @Test public void testSAXTransformerFactory() throws TransformerConfigurationException { - final String xsl = "\n" + "\n" - + " Hello World!\n" + "\n"; - + final String xsl = + """ + + + Hello World! + + """; ReaderStub.used = false; TransformerFactory transFactory = TransformerFactory.newInstance(); diff --git a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4848653.java b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4848653.java index b8fe9e06814..edf2ef2d1c1 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4848653.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4848653.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,28 +22,27 @@ */ package test.gaptest; -import static jaxp.library.JAXPTestUtilities.filenameToURL; -import static test.gaptest.GapTestConst.XML_DIR; - -import java.io.IOException; - -import javax.xml.XMLConstants; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.XMLReader; +import javax.xml.XMLConstants; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import java.io.IOException; +import java.nio.file.Path; + +import static test.gaptest.GapTestConst.XML_DIR; + /* * @test * @bug 4848653 * @library /javax/xml/jaxp/libs - * @run testng/othervm test.gaptest.Bug4848653 + * @run junit/othervm test.gaptest.Bug4848653 * @summary Verify JAXP schemaLanguage property is ignored if setValidating(false) */ public class Bug4848653 { @@ -56,7 +55,8 @@ public class Bug4848653 { parser.setProperty("http://java.sun.com/xml/jaxp/properties/schemaLanguage", XMLConstants.W3C_XML_SCHEMA_NS_URI); String filename = XML_DIR + "Bug4848653.xml"; - InputSource is = new InputSource(filenameToURL(filename)); + String uri = Path.of(filename).toUri().toASCIIString(); + InputSource is = new InputSource(uri); XMLReader xmlReader = parser.getXMLReader(); xmlReader.setErrorHandler(new MyErrorHandler()); xmlReader.parse(is); diff --git a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4858685.java b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4858685.java index 6ebcae5940d..7667d6ad140 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4858685.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4858685.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,43 +22,43 @@ */ package test.gaptest; -import static jaxp.library.JAXPTestUtilities.filenameToURL; -import static org.testng.Assert.assertEquals; -import static test.gaptest.GapTestConst.GOLDEN_DIR; -import static test.gaptest.GapTestConst.XML_DIR; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; +import org.junit.jupiter.api.Test; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.stream.StreamSource; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; -import org.testng.annotations.Test; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static test.gaptest.GapTestConst.GOLDEN_DIR; +import static test.gaptest.GapTestConst.XML_DIR; /* * @test * @bug 4858685 4894410 * @library /javax/xml/jaxp/libs - * @run testng/othervm test.gaptest.Bug4858685 + * @run junit/othervm test.gaptest.Bug4858685 * @summary test transforming text node */ public class Bug4858685 { @Test public void test() throws TransformerException, IOException { - String uri = XML_DIR + "certificate.xml"; + String filename = XML_DIR + "certificate.xml"; TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); // use URI as a StreamSource - StreamSource streamSource = new StreamSource(filenameToURL(uri)); + String uri = Path.of(filename).toUri().toASCIIString(); + StreamSource streamSource = new StreamSource(uri); DOMResult domResult = new DOMResult(); @@ -66,12 +66,8 @@ public class Bug4858685 { transformer.transform(streamSource, domResult); // dump DOM in a human readable form - String gotString = DOMDump.dumpDom(domResult.getNode()); - String goldenString = new String(Files.readAllBytes(Paths.get(GOLDEN_DIR + "Bug4858685.txt"))); - - assertEquals(gotString, goldenString); - + assertEquals(goldenString, DOMDump.dumpDom(domResult.getNode())); } /** diff --git a/test/jaxp/javax/xml/jaxp/isolatedjdk/catalog/PropertiesTest.java b/test/jaxp/javax/xml/jaxp/isolatedjdk/catalog/PropertiesTest.java index 3c871d703bf..c6e6e2a6745 100644 --- a/test/jaxp/javax/xml/jaxp/isolatedjdk/catalog/PropertiesTest.java +++ b/test/jaxp/javax/xml/jaxp/isolatedjdk/catalog/PropertiesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -23,6 +23,19 @@ package catalog; +import org.junit.jupiter.api.Test; + +import javax.xml.catalog.CatalogResolver; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import static catalog.CatalogTestUtils.DEFER_FALSE; import static catalog.CatalogTestUtils.FEATURE_DEFER; import static catalog.CatalogTestUtils.FEATURE_FILES; @@ -36,27 +49,14 @@ import static catalog.CatalogTestUtils.createPropsContent; import static catalog.CatalogTestUtils.deleteJAXPProps; import static catalog.CatalogTestUtils.generateJAXPProps; import static catalog.CatalogTestUtils.getCatalogPath; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.xml.catalog.CatalogResolver; - -import org.testng.Assert; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; /* * @test * @library /javax/xml/jaxp/libs /javax/xml/jaxp/isolatedjdk * @run shell/timeout=600 ../IsolatedJDK.sh JAXP_PROPS - * @run testng catalog.PropertiesTest + * @run junit catalog.PropertiesTest * @run shell/timeout=600 ../IsolatedJDK.sh JAXP_PROPS remove * @summary This test case tests if the properties FILES, DEFER, PREFER, * RESOLVE in jaxp.properties and system properties are used. @@ -75,11 +75,11 @@ public class PropertiesTest { public void test() throws Exception { // get required properties and do some assertions String javaclasspath = System.getProperty("java.class.path"); - Assert.assertNotNull(javaclasspath, "Test class path is null"); + assertNotNull(javaclasspath, "Test class path is null"); String testclasspath = System.getProperty("test.class.path"); - Assert.assertNotNull(testclasspath, "Test class path is null"); + assertNotNull(testclasspath, "Test class path is null"); String testsourcepath = System.getProperty("test.src"); - Assert.assertNotNull(testsourcepath, "Test source path is null"); + assertNotNull(testsourcepath, "Test source path is null"); // start the child process List testCall = new ArrayList<>(6); @@ -123,7 +123,7 @@ public class PropertiesTest { // trace exit value and assert 0 int exitValue = test.exitValue(); System.out.println("Process Exit code: " + exitValue); - Assert.assertEquals(exitValue, 0, "PropertiesTest returned nonzero exit code."); + assertEquals(0, exitValue, "PropertiesTest returned nonzero exit code."); } public static void main(String[] args) throws Exception { diff --git a/test/jaxp/javax/xml/jaxp/libs/javax/xml/parsers/ptests/MyCHandler.java b/test/jaxp/javax/xml/jaxp/libs/javax/xml/parsers/ptests/MyCHandler.java index c43390d43b9..9a32b0a1ac1 100644 --- a/test/jaxp/javax/xml/jaxp/libs/javax/xml/parsers/ptests/MyCHandler.java +++ b/test/jaxp/javax/xml/jaxp/libs/javax/xml/parsers/ptests/MyCHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -22,22 +22,23 @@ */ package javax.xml.parsers.ptests; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import static jaxp.library.JAXPTestUtilities.ERROR_MSG_HEADER; import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.LocatorImpl; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + /** * Customized DefaultHandler which writes output document when methods are * called by Transformer. Test may use output document to compare with golden * file for verification. */ class MyCHandler extends DefaultHandler implements AutoCloseable { + private static final String ERROR_MSG_HEADER = "Unexcepted exception thrown:"; private final BufferedWriter bWriter; private final Locator locator = new LocatorImpl(); diff --git a/test/jaxp/javax/xml/jaxp/libs/javax/xml/parsers/ptests/ParserTestConst.java b/test/jaxp/javax/xml/jaxp/libs/javax/xml/parsers/ptests/ParserTestConst.java index ac50221fec8..dc5ea806c9d 100644 --- a/test/jaxp/javax/xml/jaxp/libs/javax/xml/parsers/ptests/ParserTestConst.java +++ b/test/jaxp/javax/xml/jaxp/libs/javax/xml/parsers/ptests/ParserTestConst.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -22,25 +22,28 @@ */ package javax.xml.parsers.ptests; -import static jaxp.library.JAXPTestUtilities.FILE_SEP; -import static jaxp.library.JAXPTestUtilities.getPathByClassName; - +import java.io.File; +import java.nio.file.Path; /** * Utility interface which includes final variables of XML, golden file * directories. */ public class ParserTestConst { + private static final Path SRC_ROOT = Path.of(System.getProperty("test.src")).toAbsolutePath(); + + private static String forwardSlashDir(Path p) { + // Convention in these tests is to include trailing '/' in directory strings. + return p.toString().replace(File.separatorChar, '/') + '/'; + } + /** * XML source file directory. */ - public static final String XML_DIR = getPathByClassName(ParserTestConst.class, - ".." + FILE_SEP + "xmlfiles"); - + public static final String XML_DIR = forwardSlashDir(SRC_ROOT.resolveSibling("xmlfiles")); /** * Golden validation files directory. */ - public static final String GOLDEN_DIR = getPathByClassName(ParserTestConst.class, - ".." + FILE_SEP + "xmlfiles" + FILE_SEP + "out"); + public static final String GOLDEN_DIR = XML_DIR + "out/"; } diff --git a/test/jaxp/javax/xml/jaxp/libs/javax/xml/transform/ptests/TransformerTestConst.java b/test/jaxp/javax/xml/jaxp/libs/javax/xml/transform/ptests/TransformerTestConst.java index 5f119360432..23c2a73a3b2 100644 --- a/test/jaxp/javax/xml/jaxp/libs/javax/xml/transform/ptests/TransformerTestConst.java +++ b/test/jaxp/javax/xml/jaxp/libs/javax/xml/transform/ptests/TransformerTestConst.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,23 +22,27 @@ */ package javax.xml.transform.ptests; -import static jaxp.library.JAXPTestUtilities.FILE_SEP; -import static jaxp.library.JAXPTestUtilities.getPathByClassName; +import java.io.File; +import java.nio.file.Path; /** * This is the Base test class provide basic support for JAXP functional test */ public class TransformerTestConst { + private static final Path SRC_ROOT = Path.of(System.getProperty("test.src")).toAbsolutePath(); + + private static String forwardSlashDir(Path p) { + // Convention in these tests is to include trailing '/' in directory strings. + return p.toString().replace(File.separatorChar, '/') + '/'; + } + /** * XML source file directory. */ - public static final String XML_DIR = getPathByClassName(TransformerTestConst.class, - ".." + FILE_SEP + "xmlfiles"); - + public static final String XML_DIR = forwardSlashDir(SRC_ROOT.resolveSibling("xmlfiles")); /** * Golden validation files directory. */ - public static final String GOLDEN_DIR = getPathByClassName(TransformerTestConst.class, - ".." + FILE_SEP + "xmlfiles" + FILE_SEP + "out"); + public static final String GOLDEN_DIR = XML_DIR + "out/"; } diff --git a/test/jaxp/javax/xml/jaxp/libs/javax/xml/validation/ptests/ValidationTestConst.java b/test/jaxp/javax/xml/jaxp/libs/javax/xml/validation/ptests/ValidationTestConst.java index 50f4c86d0e5..f8c6aba18d3 100644 --- a/test/jaxp/javax/xml/jaxp/libs/javax/xml/validation/ptests/ValidationTestConst.java +++ b/test/jaxp/javax/xml/jaxp/libs/javax/xml/validation/ptests/ValidationTestConst.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -22,22 +22,22 @@ */ package javax.xml.validation.ptests; -import static jaxp.library.JAXPTestUtilities.FILE_SEP; -import static jaxp.library.JAXPTestUtilities.getPathByClassName; +import java.io.File; +import java.nio.file.Path; /** * This class defines the path constant */ public class ValidationTestConst { + private static final Path SRC_ROOT = Path.of(System.getProperty("test.src")).toAbsolutePath(); + + private static String forwardSlashDir(Path p) { + // Convention in these tests is to include trailing '/' in directory strings. + return p.toString().replace(File.separatorChar, '/') + '/'; + } + /** * XML source file directory. */ - public static final String XML_DIR = getPathByClassName(ValidationTestConst.class, - ".." + FILE_SEP + "xmlfiles"); - - /** - * Golden validation files directory. - */ - public static final String GOLDEN_DIR = getPathByClassName(ValidationTestConst.class, - ".." + FILE_SEP + "xmlfiles" + FILE_SEP + "out"); + public static final String XML_DIR = forwardSlashDir(SRC_ROOT.resolveSibling("xmlfiles")); } diff --git a/test/jaxp/javax/xml/jaxp/libs/javax/xml/xpath/ptests/XPathTestConst.java b/test/jaxp/javax/xml/jaxp/libs/javax/xml/xpath/ptests/XPathTestConst.java index 605dada1656..cfbb4a869df 100644 --- a/test/jaxp/javax/xml/jaxp/libs/javax/xml/xpath/ptests/XPathTestConst.java +++ b/test/jaxp/javax/xml/jaxp/libs/javax/xml/xpath/ptests/XPathTestConst.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -22,16 +22,16 @@ */ package javax.xml.xpath.ptests; -import static jaxp.library.JAXPTestUtilities.FILE_SEP; -import static jaxp.library.JAXPTestUtilities.getPathByClassName; +import java.nio.file.Path; /** * This is the Base test class provide basic support for XPath functional test */ public class XPathTestConst { + private static final Path SRC_ROOT = Path.of(System.getProperty("test.src")).toAbsolutePath(); + /** * XML source file directory. */ - public static final String XML_DIR = getPathByClassName(XPathTestConst.class, - ".." + FILE_SEP + "xmlfiles"); + public static final Path XML_DIR = SRC_ROOT.resolveSibling("xmlfiles"); } diff --git a/test/jaxp/javax/xml/jaxp/libs/jaxp/library/JAXPDataProvider.java b/test/jaxp/javax/xml/jaxp/libs/jaxp/library/JAXPDataProvider.java index d09c3d78b0b..9f925334895 100644 --- a/test/jaxp/javax/xml/jaxp/libs/jaxp/library/JAXPDataProvider.java +++ b/test/jaxp/javax/xml/jaxp/libs/jaxp/library/JAXPDataProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -23,16 +23,14 @@ package jaxp.library; -import org.testng.annotations.DataProvider; - /** * Provide invalid parameters for negative testing Factory.newInstance. */ public class JAXPDataProvider { - - @DataProvider(name = "new-instance-neg") - public static Object[][] getNewInstanceNeg() { - return new Object[][] { { null, null }, { null, JAXPDataProvider.class.getClassLoader() } }; + public static Object[][] newInstanceNeg() { + return new Object[][] { + { null, null }, + { null, JAXPDataProvider.class.getClassLoader() }, + }; } - } diff --git a/test/jaxp/javax/xml/jaxp/libs/org/w3c/dom/ptests/DOMTestUtil.java b/test/jaxp/javax/xml/jaxp/libs/org/w3c/dom/ptests/DOMTestUtil.java index 6b32aa36e74..c4239606135 100644 --- a/test/jaxp/javax/xml/jaxp/libs/org/w3c/dom/ptests/DOMTestUtil.java +++ b/test/jaxp/javax/xml/jaxp/libs/org/w3c/dom/ptests/DOMTestUtil.java @@ -22,11 +22,9 @@ */ package org.w3c.dom.ptests; -import static jaxp.library.JAXPTestUtilities.FILE_SEP; -import static jaxp.library.JAXPTestUtilities.getPathByClassName; - import java.io.File; import java.io.IOException; +import java.nio.file.Path; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -39,15 +37,22 @@ import org.xml.sax.SAXException; * This class defines the path constant and common method */ public class DOMTestUtil { + private static final Path TEST_SRC = Path.of(System.getProperty("test.src")).toAbsolutePath(); + + private static String forwardSlashDir(Path p) { + // Convention in these tests is to include trailing '/' in directory strings. + return p.toString().replace(File.separatorChar, '/') + '/'; + } + /* * XML source file directory. */ - public static final String XML_DIR = getPathByClassName(DOMTestUtil.class, ".." + FILE_SEP + "xmlfiles"); + public static final String XML_DIR = forwardSlashDir(TEST_SRC.resolveSibling("xmlfiles")); /* * Golden validation files directory. */ - public static final String GOLDEN_DIR = getPathByClassName(DOMTestUtil.class, ".." + FILE_SEP + "xmlfiles" + FILE_SEP + "out"); + public static final String GOLDEN_DIR = XML_DIR + "out/"; /* * Error Message for DOMException being expected. diff --git a/test/jaxp/javax/xml/jaxp/libs/org/xml/sax/ptests/SAXTestConst.java b/test/jaxp/javax/xml/jaxp/libs/org/xml/sax/ptests/SAXTestConst.java index 6798d7879dc..ea35a484cdf 100644 --- a/test/jaxp/javax/xml/jaxp/libs/org/xml/sax/ptests/SAXTestConst.java +++ b/test/jaxp/javax/xml/jaxp/libs/org/xml/sax/ptests/SAXTestConst.java @@ -22,8 +22,8 @@ */ package org.xml.sax.ptests; -import static jaxp.library.JAXPTestUtilities.FILE_SEP; -import static jaxp.library.JAXPTestUtilities.getPathByClassName; +import java.io.File; +import java.nio.file.Path; /** * This is the Base test class provide basic support for JAXP SAX functional @@ -31,16 +31,20 @@ import static jaxp.library.JAXPTestUtilities.getPathByClassName; * has their own TestBase class. */ public class SAXTestConst { + private static final Path TEST_SRC = Path.of(System.getProperty("test.src")).toAbsolutePath(); + + private static String forwardSlashDir(Path p) { + // Convention in these tests is to include trailing '/' in directory strings. + return p.toString().replace(File.separatorChar, '/') + '/'; + } + /** * XML source file directory. */ - public static final String XML_DIR = getPathByClassName(SAXTestConst.class, - ".." + FILE_SEP + "xmlfiles"); - + public static final String XML_DIR = forwardSlashDir(TEST_SRC.resolveSibling("xmlfiles")); /** * Golden validation files directory. */ - public static final String GOLDEN_DIR = getPathByClassName(SAXTestConst.class, - ".." + FILE_SEP + "xmlfiles" + FILE_SEP + "out"); + public static final String GOLDEN_DIR = XML_DIR + "out/"; } diff --git a/test/jaxp/javax/xml/jaxp/libs/test/astro/AstroConstants.java b/test/jaxp/javax/xml/jaxp/libs/test/astro/AstroConstants.java index b3f2cb6e5c6..9645832c896 100644 --- a/test/jaxp/javax/xml/jaxp/libs/test/astro/AstroConstants.java +++ b/test/jaxp/javax/xml/jaxp/libs/test/astro/AstroConstants.java @@ -23,10 +23,14 @@ package test.astro; +import java.nio.file.Path; + import static java.io.File.separator; -import static jaxp.library.JAXPTestUtilities.getPathByClassName; public class AstroConstants { + private static final Path XML_FILES = + Path.of(System.getProperty("test.src")).resolve("xmlfiles").toAbsolutePath(); + // Query parameters : public static final double RA_MIN = 0.0; // hours @@ -36,7 +40,7 @@ public class AstroConstants { // Stylesheet source paths: - public static final String XSLPATH = getPathByClassName(AstroConstants.class, "xmlfiles" + separator + "xsl"); + public static final String XSLPATH = XML_FILES.resolve("xsl").toString() + separator; public static final String RAXSL = XSLPATH + "ra.xsl"; public static final String DECXSL = XSLPATH + "dec.xsl"; public static final String RADECXSL = XSLPATH + "radec.xsl"; @@ -51,10 +55,9 @@ public class AstroConstants { // Catalog references - public static final String ASTROCAT = getPathByClassName(AstroConstants.class, "xmlfiles") + "catalog.xml"; + public static final String ASTROCAT = XML_FILES.resolve("catalog.xml").toString(); - - public static final String GOLDEN_DIR = getPathByClassName(AstroConstants.class, "xmlfiles" + separator + "gold"); + public static final String GOLDEN_DIR = XML_FILES.resolve("gold").toString() + separator; public static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; public static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource"; } diff --git a/test/jaxp/javax/xml/jaxp/libs/test/astro/DOMFilterFactoryImpl.java b/test/jaxp/javax/xml/jaxp/libs/test/astro/DOMFilterFactoryImpl.java index e232fb61fcc..32c34673846 100644 --- a/test/jaxp/javax/xml/jaxp/libs/test/astro/DOMFilterFactoryImpl.java +++ b/test/jaxp/javax/xml/jaxp/libs/test/astro/DOMFilterFactoryImpl.java @@ -22,12 +22,12 @@ */ package test.astro; -import static jaxp.library.JAXPTestUtilities.filenameToURL; import static test.astro.AstroConstants.DECXSL; import static test.astro.AstroConstants.RAXSL; import static test.astro.AstroConstants.STYPEXSL; import java.io.IOException; +import java.nio.file.Path; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -75,6 +75,7 @@ public class DOMFilterFactoryImpl extends SourceFilterFactory { private Document getStylesheetDOM(String xslfilename) throws SAXException, IOException, ParserConfigurationException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); - return dbf.newDocumentBuilder().parse(filenameToURL(xslfilename)); + String xslUri = Path.of(xslfilename).toUri().toASCIIString(); + return dbf.newDocumentBuilder().parse(xslUri); } } diff --git a/test/jaxp/javax/xml/jaxp/libs/test/astro/DOML3InputSourceFactoryImpl.java b/test/jaxp/javax/xml/jaxp/libs/test/astro/DOML3InputSourceFactoryImpl.java index 2f4170b21ce..6fde37c79ab 100644 --- a/test/jaxp/javax/xml/jaxp/libs/test/astro/DOML3InputSourceFactoryImpl.java +++ b/test/jaxp/javax/xml/jaxp/libs/test/astro/DOML3InputSourceFactoryImpl.java @@ -22,22 +22,6 @@ */ package test.astro; -import static jaxp.library.JAXPTestUtilities.filenameToURL; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static org.w3c.dom.ls.DOMImplementationLS.MODE_SYNCHRONOUS; -import static org.w3c.dom.traversal.NodeFilter.SHOW_ELEMENT; - -import java.io.ByteArrayInputStream; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.InputStreamReader; -import java.io.Reader; -import java.nio.file.Files; -import java.nio.file.Paths; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - import org.w3c.dom.DOMConfiguration; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -50,6 +34,20 @@ import org.w3c.dom.ls.LSSerializer; import org.w3c.dom.ls.LSSerializerFilter; import org.xml.sax.InputSource; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.ByteArrayInputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.w3c.dom.ls.DOMImplementationLS.MODE_SYNCHRONOUS; +import static org.w3c.dom.traversal.NodeFilter.SHOW_ELEMENT; + /* * A specialized implementation of an Input Source factory that utilizes * DOM Level 3 implementations to build a Document (DOM) from the @@ -72,7 +70,7 @@ public class DOML3InputSourceFactoryImpl implements InputSourceFactory { Document doc = null; LSInput src = impl.createLSInput(); // register the input file with the input source... - String systemId = filenameToURL(filename); + String systemId = Path.of(filename).toUri().toASCIIString(); src.setSystemId(systemId); try (Reader reader = new FileReader(filename)) { src.setCharacterStream(reader); @@ -82,7 +80,7 @@ public class DOML3InputSourceFactoryImpl implements InputSourceFactory { // Use DOM L3 LSSerializer (previously called a DOMWriter) // to serialize the xml doc DOM to a file stream. - String tmpCatalog = Files.createTempFile(Paths.get(USER_DIR), "catalog.xml", null).toString(); + String tmpCatalog = Files.createTempFile(Paths.get("."), "catalog.xml", null).toString(); LSSerializer domserializer = impl.createLSSerializer(); domserializer.setFilter(new MyDOMWriterFilter()); diff --git a/test/jaxp/javax/xml/jaxp/libs/test/astro/InputSourceFactoryImpl.java b/test/jaxp/javax/xml/jaxp/libs/test/astro/InputSourceFactoryImpl.java index a2c5b8319c8..d5ccdbf70e6 100644 --- a/test/jaxp/javax/xml/jaxp/libs/test/astro/InputSourceFactoryImpl.java +++ b/test/jaxp/javax/xml/jaxp/libs/test/astro/InputSourceFactoryImpl.java @@ -22,10 +22,10 @@ */ package test.astro; -import static jaxp.library.JAXPTestUtilities.filenameToURL; - import org.xml.sax.InputSource; +import java.nio.file.Path; + /* * Default implementation of a input source factory. This is the most * straight forward way to create a sax input source and set it's @@ -38,7 +38,8 @@ public class InputSourceFactoryImpl implements InputSourceFactory { public InputSource newInputSource(String filename) { InputSource catSrc = new InputSource(filename); - catSrc.setSystemId(filenameToURL(filename)); + String uri = Path.of(filename).toUri().toASCIIString(); + catSrc.setSystemId(uri); return catSrc; } } diff --git a/test/jaxp/javax/xml/jaxp/libs/test/astro/SAXFilterFactoryImpl.java b/test/jaxp/javax/xml/jaxp/libs/test/astro/SAXFilterFactoryImpl.java index 4ef5d3cc259..403c0a70460 100644 --- a/test/jaxp/javax/xml/jaxp/libs/test/astro/SAXFilterFactoryImpl.java +++ b/test/jaxp/javax/xml/jaxp/libs/test/astro/SAXFilterFactoryImpl.java @@ -22,22 +22,22 @@ */ package test.astro; -import static jaxp.library.JAXPTestUtilities.filenameToURL; -import static test.astro.AstroConstants.DECENTXSL; -import static test.astro.AstroConstants.DECXSL; -import static test.astro.AstroConstants.RAENTXSL; -import static test.astro.AstroConstants.STYPEXSL; -import static test.astro.AstroConstants.TOPTEMPLXSL; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.Source; import javax.xml.transform.sax.SAXSource; +import java.nio.file.Path; -import org.xml.sax.EntityResolver; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; +import static test.astro.AstroConstants.DECENTXSL; +import static test.astro.AstroConstants.DECXSL; +import static test.astro.AstroConstants.RAENTXSL; +import static test.astro.AstroConstants.STYPEXSL; +import static test.astro.AstroConstants.TOPTEMPLXSL; /* * Implementation of the filter factory interface that utilizes SAX @@ -51,7 +51,7 @@ import org.xml.sax.XMLReader; * */ public class SAXFilterFactoryImpl extends SourceFilterFactory { - private EntityResolver entityResolver; + private final EntityResolver entityResolver; public SAXFilterFactoryImpl() { super(); @@ -60,7 +60,8 @@ public class SAXFilterFactoryImpl extends SourceFilterFactory { @Override protected Source getSource(String xslFileName) throws SAXException, ParserConfigurationException { - SAXSource saxsource = new SAXSource(new InputSource(filenameToURL(xslFileName))); + String xslUri = Path.of(xslFileName).toUri().toASCIIString(); + SAXSource saxsource = new SAXSource(new InputSource(xslUri)); saxsource.setXMLReader(getXMLReader()); return saxsource; } @@ -97,7 +98,8 @@ public class SAXFilterFactoryImpl extends SourceFilterFactory { public InputSource resolveEntity(String publicid, String sysId) { if (sysId.equals("http://astro.com/stylesheets/toptemplate")) { InputSource retval = new InputSource(TOPTEMPLXSL); - retval.setSystemId(filenameToURL(TOPTEMPLXSL)); + String xslUri = Path.of(TOPTEMPLXSL).toUri().toASCIIString(); + retval.setSystemId(xslUri); return retval; } else { return null; // use default behavior diff --git a/test/jaxp/javax/xml/jaxp/libs/test/astro/StreamFilterFactoryImpl.java b/test/jaxp/javax/xml/jaxp/libs/test/astro/StreamFilterFactoryImpl.java index 0978161312e..38e864f6897 100644 --- a/test/jaxp/javax/xml/jaxp/libs/test/astro/StreamFilterFactoryImpl.java +++ b/test/jaxp/javax/xml/jaxp/libs/test/astro/StreamFilterFactoryImpl.java @@ -22,7 +22,6 @@ */ package test.astro; -import static jaxp.library.JAXPTestUtilities.filenameToURL; import static test.astro.AstroConstants.DECXSL; import static test.astro.AstroConstants.RADECXSL; import static test.astro.AstroConstants.RAXSL; @@ -30,11 +29,13 @@ import static test.astro.AstroConstants.STYPEXSL; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; +import java.nio.file.Path; public class StreamFilterFactoryImpl extends SourceFilterFactory { @Override protected Source getSource(String xslFileName) { - return new StreamSource(filenameToURL(xslFileName)); + String xslUri = Path.of(xslFileName).toUri().toASCIIString(); + return new StreamSource(xslUri); } @Override diff --git a/test/jaxp/javax/xml/jaxp/libs/test/astro/TemplatesFilterFactoryImpl.java b/test/jaxp/javax/xml/jaxp/libs/test/astro/TemplatesFilterFactoryImpl.java index 09a71b1a534..6047d0c5ae8 100644 --- a/test/jaxp/javax/xml/jaxp/libs/test/astro/TemplatesFilterFactoryImpl.java +++ b/test/jaxp/javax/xml/jaxp/libs/test/astro/TemplatesFilterFactoryImpl.java @@ -22,13 +22,13 @@ */ package test.astro; -import static jaxp.library.JAXPTestUtilities.filenameToURL; import static test.astro.AstroConstants.DECXSL; import static test.astro.AstroConstants.RAURIXSL; import static test.astro.AstroConstants.STYPEXSL; import static test.astro.AstroConstants.TOPTEMPLINCXSL; import java.io.IOException; +import java.nio.file.Path; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; @@ -94,7 +94,8 @@ public class TemplatesFilterFactoryImpl extends AbstractFilterFactory { // create the stylesheet input source InputSource xslSrc = new InputSource(xslFileName); - xslSrc.setSystemId(filenameToURL(xslFileName)); + String xslUri = Path.of(xslFileName).toUri().toASCIIString(); + xslSrc.setSystemId(xslUri); // hook up the templates handler as the xsl content handler xmlreader.setContentHandler(templatesHandler); // call parse on the xsl input source @@ -113,7 +114,8 @@ public class TemplatesFilterFactoryImpl extends AbstractFilterFactory { public Source resolve(String href, String base) throws TransformerException { if ("http://astro.com/stylesheets/topleveltemplate".equals(href)) { StreamSource ss = new StreamSource(TOPTEMPLINCXSL); - ss.setSystemId(filenameToURL(TOPTEMPLINCXSL)); + String xslUri = Path.of(TOPTEMPLINCXSL).toUri().toASCIIString(); + ss.setSystemId(xslUri); return ss; } else { return null; diff --git a/test/jaxp/javax/xml/jaxp/libs/test/auctionportal/HiBidConstants.java b/test/jaxp/javax/xml/jaxp/libs/test/auctionportal/HiBidConstants.java index 5c812c52b04..7852540a308 100644 --- a/test/jaxp/javax/xml/jaxp/libs/test/auctionportal/HiBidConstants.java +++ b/test/jaxp/javax/xml/jaxp/libs/test/auctionportal/HiBidConstants.java @@ -22,21 +22,29 @@ */ package test.auctionportal; -import static jaxp.library.JAXPTestUtilities.getPathByClassName; +import java.io.File; +import java.nio.file.Path; /** * This is the Base test class provide basic support for Auction portal test. */ public class HiBidConstants { + private static final Path TEST_SRC = Path.of(System.getProperty("test.src")).toAbsolutePath(); + + private static String forwardSlashDir(Path p) { + // Convention in these tests is to include trailing '/' in directory strings. + return p.toString().replace(File.separatorChar, '/') + '/'; + } + /** * XML source file directory. */ - public static final String XML_DIR = getPathByClassName(HiBidConstants.class, "content"); + public static final String XML_DIR = forwardSlashDir(TEST_SRC.resolve("content")); /** * Golden validation files directory. */ - public static final String GOLDEN_DIR = getPathByClassName(HiBidConstants.class, "golden"); + public static final String GOLDEN_DIR = forwardSlashDir(TEST_SRC.resolve("golden")); /** * Name space for account operation. diff --git a/test/jaxp/javax/xml/jaxp/libs/test/gaptest/GapTestConst.java b/test/jaxp/javax/xml/jaxp/libs/test/gaptest/GapTestConst.java index 237d0ac6ed2..648a6c2be10 100644 --- a/test/jaxp/javax/xml/jaxp/libs/test/gaptest/GapTestConst.java +++ b/test/jaxp/javax/xml/jaxp/libs/test/gaptest/GapTestConst.java @@ -22,20 +22,27 @@ */ package test.gaptest; -import static jaxp.library.JAXPTestUtilities.FILE_SEP; -import static jaxp.library.JAXPTestUtilities.getPathByClassName; +import java.io.File; +import java.nio.file.Path; /** * This class defines the path constant */ public class GapTestConst { + private static final Path TEST_SRC = Path.of(System.getProperty("test.src")).toAbsolutePath(); + + private static String forwardSlashDir(Path p) { + // Convention in these tests is to include trailing '/' in directory strings. + return p.toString().replace(File.separatorChar, '/') + '/'; + } + /** * XML source file directory. */ - public static final String XML_DIR = getPathByClassName(GapTestConst.class, "xmlfiles"); + public static final String XML_DIR = forwardSlashDir(TEST_SRC.resolve("xmlfiles")); /** * Golden validation files directory. */ - public static final String GOLDEN_DIR = getPathByClassName(GapTestConst.class, "xmlfiles" + FILE_SEP + "out"); + public static final String GOLDEN_DIR = XML_DIR + "out/"; } diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 070f9b0ce03..38ffe2ae963 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -222,6 +222,7 @@ sun/awt/datatransfer/SuplementaryCharactersTransferTest.java 8011371 generic-all sun/awt/shell/ShellFolderMemoryLeak.java 8197794 windows-all sun/java2d/DirectX/OverriddenInsetsTest/OverriddenInsetsTest.java 8196102 generic-all sun/java2d/DirectX/RenderingToCachedGraphicsTest/RenderingToCachedGraphicsTest.java 8196180 windows-all,macosx-all +sun/java2d/OpenGL/MultiWindowFillTest.java 8378506 macosx-all sun/java2d/OpenGL/OpaqueDest.java#id1 8367574 macosx-all sun/java2d/OpenGL/ScaleParamsOOB.java#id0 8377908 linux-all sun/java2d/SunGraphics2D/EmptyClipRenderingTest.java 8144029 macosx-all,linux-all @@ -569,6 +570,8 @@ java/nio/channels/DatagramChannel/Unref.java 8233437 generic- java/nio/channels/DatagramChannel/BasicMulticastTests.java 8144003 macosx-all java/nio/channels/DatagramChannel/MulticastSendReceiveTests.java 8144003 macosx-all java/nio/channels/DatagramChannel/Promiscuous.java 8144003 macosx-all +java/nio/channels/SocketChannel/CloseDuringConnect.java 8375658 macosx-26.0,macosx-26.1,macosx-26.2,macosx-26.3 +java/nio/channels/SocketChannel/OpenLeak.java 8375658 macosx-26.0,macosx-26.1,macosx-26.2,macosx-26.3 ############################################################################ @@ -755,7 +758,6 @@ java/awt/FileDialog/DefaultFocusOwner/DefaultFocusOwner.java 7187728 macosx-all, java/awt/FileDialog/DoubleActionESC.java 8356981 linux-all java/awt/print/PageFormat/Orient.java 8016055 macosx-all java/awt/TextArea/TextAreaCursorTest/HoveringAndDraggingTest.java 8024986 macosx-all,linux-all -java/awt/TextComponent/CorrectTextComponentSelectionTest.java 8237220 macosx-all java/awt/TextComponent/SelectionAndCaretColor.java 7017622 linux-all java/awt/event/MouseEvent/SpuriousExitEnter/SpuriousExitEnter.java 8254841 macosx-all java/awt/FullScreen/TranslucentWindow/TranslucentWindow.java 8258103 linux-all diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups index ccf745700d1..2bff07b8a55 100644 --- a/test/jdk/TEST.groups +++ b/test/jdk/TEST.groups @@ -558,6 +558,7 @@ jdk_jfr_sanity = \ jdk/jfr/event/gc/collection/TestGCWithFasttime.java \ jdk/jfr/event/gc/configuration/TestGCConfigurationEvent.java \ jdk/jfr/event/metadata/TestDefaultConfigurations.java \ + jdk/jfr/event/oldobject/TestDFSWithSmallStack.java \ jdk/jfr/startupargs/TestDumpOnExit.java \ jdk/jfr/api/consumer/recordingstream/TestBasics.java diff --git a/test/jdk/com/sun/crypto/provider/Cipher/KeyWrap/NISTWrapKAT.java b/test/jdk/com/sun/crypto/provider/Cipher/KeyWrap/NISTWrapKAT.java index a79f48196d1..09fd0c440a9 100644 --- a/test/jdk/com/sun/crypto/provider/Cipher/KeyWrap/NISTWrapKAT.java +++ b/test/jdk/com/sun/crypto/provider/Cipher/KeyWrap/NISTWrapKAT.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2026, 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 @@ -24,20 +24,21 @@ /* * @test * @bug 5008156 8248268 - * @run testng NISTWrapKAT + * @run junit NISTWrapKAT * @summary Verify that the AES-Key-Wrap and AES-Key-Wrap-Pad ciphers * work as expected using NIST test vectors. * @author Valerie Peng */ import java.security.Key; import java.security.AlgorithmParameters; -import javax.crypto.*; -import javax.crypto.spec.*; +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; import java.util.Arrays; import java.math.BigInteger; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.testng.Assert; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class NISTWrapKAT { @@ -101,8 +102,7 @@ public class NISTWrapKAT { } } - @DataProvider - public Object[][] testData() { + static Object[][] testData() { return new Object[][] { { "AESWrap", KEK, 16, DATA, 16, KW_AES128_128 }, { "AESWrap", KEK, 24, DATA, 16, KW_AES192_128 }, @@ -249,7 +249,8 @@ public class NISTWrapKAT { }; } - @Test(dataProvider = "testData") + @ParameterizedTest + @MethodSource("testData") public void testKeyWrap(String algo, String key, int keyLen, String data, int dataLen, String expected) throws Exception { System.out.println("Testing " + algo + " Cipher with wrapping " + @@ -311,7 +312,8 @@ public class NISTWrapKAT { } } - @Test(dataProvider = "testData") + @ParameterizedTest + @MethodSource("testData") public void testEnc(String algo, String key, int keyLen, String data, int dataLen, String expected) throws Exception { System.out.println("Testing " + algo + " Cipher with enc " + diff --git a/test/jdk/com/sun/crypto/provider/KeyFactory/PBEKeyDestroyTest.java b/test/jdk/com/sun/crypto/provider/KeyFactory/PBEKeyDestroyTest.java index da266c147fb..41a86ccec64 100644 --- a/test/jdk/com/sun/crypto/provider/KeyFactory/PBEKeyDestroyTest.java +++ b/test/jdk/com/sun/crypto/provider/KeyFactory/PBEKeyDestroyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -26,13 +26,17 @@ * @bug 8312306 * @summary Check the destroy()/isDestroyed() of the PBEKey impl from SunJCE * @library /test/lib - * @run testng/othervm PBEKeyDestroyTest + * @run junit/othervm PBEKeyDestroyTest */ -import javax.crypto.*; -import javax.crypto.spec.*; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; import java.nio.charset.StandardCharsets; -import org.testng.Assert; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; public class PBEKeyDestroyTest { @@ -48,22 +52,22 @@ public class PBEKeyDestroyTest { SecretKey key2 = skf.generateSecret(keySpec); // should be equal - Assert.assertFalse(key1.isDestroyed()); - Assert.assertFalse(key2.isDestroyed()); - Assert.assertTrue(key1.equals(key2)); - Assert.assertTrue(key2.equals(key1)); + assertFalse(key1.isDestroyed()); + assertFalse(key2.isDestroyed()); + assertEquals(key1, key2); + assertEquals(key2, key1); // destroy key1 key1.destroy(); - Assert.assertTrue(key1.isDestroyed()); - Assert.assertFalse(key1.equals(key2)); - Assert.assertFalse(key2.equals(key1)); + assertTrue(key1.isDestroyed()); + assertNotEquals(key1, key2); + assertNotEquals(key2, key1); // also destroy key2 key2.destroy(); - Assert.assertTrue(key2.isDestroyed()); - Assert.assertFalse(key1.equals(key2)); - Assert.assertFalse(key2.equals(key1)); + assertTrue(key2.isDestroyed()); + assertNotEquals(key1, key2); + assertNotEquals(key2, key1); // call destroy again to make sure no unexpected exceptions key2.destroy(); diff --git a/test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java b/test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java index b139f12da25..7d65298faa6 100644 --- a/test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java +++ b/test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java @@ -27,10 +27,10 @@ * @summary Multi-threaded client timeout tests for ldap pool * @library /test/lib * lib/ - * @run testng/othervm/timeout=480 LdapPoolTimeoutTest + * @run junit/othervm/timeout=480 LdapPoolTimeoutTest */ -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import javax.naming.Context; import javax.naming.NamingException; diff --git a/test/jdk/com/sun/jndi/ldap/LdapTimeoutTest.java b/test/jdk/com/sun/jndi/ldap/LdapTimeoutTest.java index 39bf24b9dbf..a4b032d0974 100644 --- a/test/jdk/com/sun/jndi/ldap/LdapTimeoutTest.java +++ b/test/jdk/com/sun/jndi/ldap/LdapTimeoutTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, 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 @@ -25,14 +25,17 @@ * @test * @library /test/lib * lib/ - * @run testng/othervm LdapTimeoutTest + * @run junit/othervm LdapTimeoutTest * @bug 7094377 8000487 6176036 7056489 8151678 * @summary Timeout tests for ldap */ -import org.testng.Assert; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; import javax.naming.Context; import javax.naming.NamingException; @@ -60,8 +63,6 @@ import static java.lang.String.format; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static jdk.test.lib.Utils.adjustTimeout; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.expectThrows; public class LdapTimeoutTest { @@ -90,7 +91,7 @@ public class LdapTimeoutTest { assert (2 * CONNECT_MILLIS + READ_MILLIS + TOLERANCE < INFINITY_MILLIS); } - @BeforeTest + @BeforeEach public void beforeTest() { startAuxiliaryDiagnosticOutput(); } @@ -98,11 +99,6 @@ public class LdapTimeoutTest { /* * These are timeout tests and they are run in parallel to reduce the total * amount of run time. - * - * Currently it doesn't seem possible to instruct JTREG to run TestNG test - * methods in parallel. That said, this JTREG test is still - * a "TestNG-flavored" test for the sake of having org.testng.Assert - * capability. */ @Test public void test() throws Exception { @@ -190,11 +186,11 @@ public class LdapTimeoutTest { env.put(Context.PROVIDER_URL, urlTo(server)); server.start(); server.starting().join(); - Assert.ThrowingRunnable completion = + Executable completion = () -> assertCompletion(CONNECT_MILLIS, 2 * CONNECT_MILLIS + TOLERANCE, () -> new InitialDirContext(env)); - NamingException e = expectThrows(NamingException.class, completion); + NamingException e = assertThrows(NamingException.class, completion); String msg = e.getMessage(); assertTrue(msg != null && msg.contains("timeout") && msg.contains(String.valueOf(CONNECT_MILLIS)), @@ -214,11 +210,11 @@ public class LdapTimeoutTest { InitialDirContext ctx = new InitialDirContext(env); SearchControls scl = new SearchControls(); scl.setSearchScope(SearchControls.SUBTREE_SCOPE); - Assert.ThrowingRunnable completion = + Executable completion = () -> assertCompletion(READ_MILLIS, READ_MILLIS + TOLERANCE, () -> ctx.search("ou=People,o=JNDITutorial", "(objectClass=*)", scl)); - NamingException e = expectThrows(NamingException.class, completion); + NamingException e = assertThrows(NamingException.class, completion); String msg = e.getMessage(); assertTrue(msg != null && msg.contains("timeout") && msg.contains(String.valueOf(READ_MILLIS)), @@ -235,11 +231,11 @@ public class LdapTimeoutTest { env.put(Context.PROVIDER_URL, urlTo(server)); server.start(); server.starting().join(); - Assert.ThrowingRunnable completion = + Executable completion = () -> assertCompletion(CONNECT_MILLIS, 2 * CONNECT_MILLIS + TOLERANCE, () -> new InitialDirContext(env)); - NamingException e = expectThrows(NamingException.class, completion); + NamingException e = assertThrows(NamingException.class, completion); String msg = e.getMessage(); assertTrue(msg != null && msg.contains("timeout") && msg.contains(String.valueOf(CONNECT_MILLIS)), @@ -261,11 +257,11 @@ public class LdapTimeoutTest { env.put(Context.PROVIDER_URL, urlTo(server)); server.start(); server.starting().join(); - Assert.ThrowingRunnable completion = + Executable completion = () -> assertCompletion(CONNECT_MILLIS, 2 * CONNECT_MILLIS + TOLERANCE, () -> new InitialDirContext(env)); - NamingException e = expectThrows(NamingException.class, completion); + NamingException e = assertThrows(NamingException.class, completion); String msg = e.getMessage(); assertTrue(msg != null && msg.contains("timeout") && msg.contains(String.valueOf(CONNECT_MILLIS)), diff --git a/test/jdk/com/sun/jndi/ldap/NamingExceptionMessageTest.java b/test/jdk/com/sun/jndi/ldap/NamingExceptionMessageTest.java index c165593ad5e..1250548b60a 100644 --- a/test/jdk/com/sun/jndi/ldap/NamingExceptionMessageTest.java +++ b/test/jdk/com/sun/jndi/ldap/NamingExceptionMessageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ * @summary Test that CommunicationException is thrown when connection is timed out or closed/cancelled, * and it's text matches the failure reason. * @library /test/lib lib - * @run testng NamingExceptionMessageTest + * @run junit NamingExceptionMessageTest */ import javax.naming.CommunicationException; @@ -45,8 +45,11 @@ import java.util.Hashtable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import org.testng.annotations.Test; -import org.testng.Assert; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import jdk.test.lib.net.URIBuilder; public class NamingExceptionMessageTest { @@ -58,9 +61,9 @@ public class NamingExceptionMessageTest { ldapServer.awaitStartup(); var env = ldapServer.getInitialLdapCtxEnvironment(TIMEOUT_VALUE); var communicationException = - Assert.expectThrows(CommunicationException.class, () -> new InitialDirContext(env)); + assertThrows(CommunicationException.class, () -> new InitialDirContext(env)); System.out.println("Got CommunicationException:" + communicationException); - Assert.assertEquals(communicationException.getMessage(), EXPECTED_TIMEOUT_MESSAGE); + assertEquals(EXPECTED_TIMEOUT_MESSAGE, communicationException.getMessage()); } } @@ -70,7 +73,7 @@ public class NamingExceptionMessageTest { ldapServer.start(); ldapServer.awaitStartup(); var env = ldapServer.getInitialLdapCtxEnvironment(0); - var namingException = Assert.expectThrows(NamingException.class, () -> new InitialDirContext(env)); + var namingException = assertThrows(NamingException.class, () -> new InitialDirContext(env)); if (namingException instanceof ServiceUnavailableException) { // If naming exception is ServiceUnavailableException it could mean // that the connection was closed on test server-side before LDAP client starts @@ -78,11 +81,11 @@ public class NamingExceptionMessageTest { System.out.println("Got ServiceUnavailableException: Test PASSED"); } else { // If exception is not ServiceUnavailableException, CommunicationException is expected - Assert.assertTrue(namingException instanceof CommunicationException); + assertInstanceOf(CommunicationException.class, namingException); var communicationException = (CommunicationException) namingException; System.out.println("Got CommunicationException:" + communicationException); // Check exception message - Assert.assertEquals(communicationException.getMessage(), EXPECTED_CLOSURE_MESSAGE); + assertEquals(EXPECTED_CLOSURE_MESSAGE, communicationException.getMessage()); } } } diff --git a/test/jdk/com/sun/net/httpserver/TaskRejectedTest.java b/test/jdk/com/sun/net/httpserver/TaskRejectedTest.java index 6b6d53fd3a3..109d28627dc 100644 --- a/test/jdk/com/sun/net/httpserver/TaskRejectedTest.java +++ b/test/jdk/com/sun/net/httpserver/TaskRejectedTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -21,11 +21,6 @@ * questions. */ -/** - * @test - * @bug 8169358 - * @summary HttpServer does not close client connection when RejectedExecutionException occurs. - */ import com.sun.net.httpserver.HttpServer; @@ -46,6 +41,14 @@ import java.util.logging.Level; import java.util.logging.Logger; import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY; +/* + * @test + * @bug 8169358 + * @summary HttpServer does not close client connection when RejectedExecutionException occurs. + * @comment We use othervm because this test configures logging handlers + * for the system wide "com.sun.net.httpserver" logger + * @run main/othervm ${test.main.class} + */ public class TaskRejectedTest { private static final int BACKLOG = 0; @@ -54,6 +57,15 @@ public class TaskRejectedTest { private static final int TIMEOUT = 10000; // 10 sec + private static final Logger logger = Logger.getLogger("com.sun.net.httpserver"); + + private static void setupLogging() { + final Handler consoleHandler = new ConsoleHandler(); + consoleHandler.setLevel(Level.FINEST); + logger.setLevel(Level.FINEST); + logger.addHandler(consoleHandler); + } + private static void runClient(InetSocketAddress listenAddr) throws MalformedURLException, IOException { URL url = new URL("http", listenAddr.getHostString(), @@ -75,13 +87,7 @@ public class TaskRejectedTest { } public static void main(String[] args) throws Exception { - Logger logger = Logger.getLogger( - HttpServer.class.getPackage().getName()); - Handler consoleHandler = new ConsoleHandler(); - consoleHandler.setLevel(Level.FINEST); - logger.setLevel(Level.FINEST); - logger.addHandler(consoleHandler); - + setupLogging(); // merely for debugging Executor executor = Executors.newSingleThreadExecutor(r -> { throw new RejectedExecutionException("test"); }); diff --git a/test/jdk/com/sun/net/httpserver/Test13.java b/test/jdk/com/sun/net/httpserver/Test13.java index 0401dbce5a8..503b9e4430c 100644 --- a/test/jdk/com/sun/net/httpserver/Test13.java +++ b/test/jdk/com/sun/net/httpserver/Test13.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -21,19 +21,6 @@ * questions. */ -/* - * @test - * @bug 6270015 - * @library /test/lib - * @build jdk.test.lib.Asserts - * jdk.test.lib.Utils - * jdk.test.lib.net.SimpleSSLContext - * jdk.test.lib.net.URIBuilder - * @run main/othervm Test13 - * @run main/othervm -Djava.net.preferIPv6Addresses=true Test13 - * @summary Light weight HTTP server - */ - import com.sun.net.httpserver.*; import java.nio.file.Files; @@ -54,6 +41,20 @@ import static jdk.test.lib.Utils.createTempFileOfSize; * - same as Test12, but with 64 threads */ +/* + * @test + * @bug 6270015 + * @summary Light weight HTTP server + * @library /test/lib + * @build jdk.test.lib.Asserts + * jdk.test.lib.Utils + * jdk.test.lib.net.SimpleSSLContext + * jdk.test.lib.net.URIBuilder + * @comment We use othervm because this test configures logging handlers + * for the system wide "com.sun.net.httpserver" logger + * @run main/othervm ${test.main.class} + * @run main/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class} + */ public class Test13 extends Test { private static final String TEMP_FILE_PREFIX = @@ -61,20 +62,25 @@ public class Test13 extends Test { private static final SSLContext ctx = SimpleSSLContext.findSSLContext(); + private static final Logger logger = Logger.getLogger ("com.sun.net.httpserver"); + final static int NUM = 32; // was 32 static boolean fail = false; + private static void setupLogging() { + final Handler handler = new ConsoleHandler(); + handler.setLevel(Level.ALL); + logger.setLevel(Level.ALL); + logger.addHandler(handler); + } + public static void main (String[] args) throws Exception { HttpServer s1 = null; HttpsServer s2 = null; ExecutorService executor=null; Path filePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 23); - Logger l = Logger.getLogger ("com.sun.net.httpserver"); - Handler ha = new ConsoleHandler(); - ha.setLevel(Level.ALL); - l.setLevel(Level.ALL); - l.addHandler(ha); + setupLogging(); // merely for debugging InetAddress loopback = InetAddress.getLoopbackAddress(); try { System.out.print ("Test13: "); diff --git a/test/jdk/com/sun/net/httpserver/Test7a.java b/test/jdk/com/sun/net/httpserver/Test7a.java index 8bda4ae8348..832238b2b47 100644 --- a/test/jdk/com/sun/net/httpserver/Test7a.java +++ b/test/jdk/com/sun/net/httpserver/Test7a.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -21,16 +21,6 @@ * questions. */ -/** - * @test - * @bug 6270015 - * @library /test/lib - * @build jdk.test.lib.net.SimpleSSLContext jdk.test.lib.net.URIBuilder - * @run main/othervm Test7a - * @run main/othervm -Djava.net.preferIPv6Addresses=true Test7a - * @summary Light weight HTTP server - */ - import com.sun.net.httpserver.*; import java.util.concurrent.*; @@ -44,15 +34,18 @@ import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY; /** * Test POST large file via chunked encoding (large chunks) */ - +/* + * @test + * @bug 6270015 + * @summary Light weight HTTP server + * @library /test/lib + * @build jdk.test.lib.net.SimpleSSLContext jdk.test.lib.net.URIBuilder + * @run main/othervm ${test.main.class} + * @run main/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class} + */ public class Test7a extends Test { public static void main (String[] args) throws Exception { - //Logger log = Logger.getLogger ("com.sun.net.httpserver"); - //log.setLevel (Level.FINE); - //ConsoleHandler h = new ConsoleHandler(); - //h.setLevel (Level.ALL); - //log.addHandler (h); Handler handler = new Handler(); InetAddress loopback = InetAddress.getLoopbackAddress(); InetSocketAddress addr = new InetSocketAddress(loopback, 0); diff --git a/test/jdk/com/sun/net/httpserver/Test8a.java b/test/jdk/com/sun/net/httpserver/Test8a.java index 67f9c93172a..9d1525352cf 100644 --- a/test/jdk/com/sun/net/httpserver/Test8a.java +++ b/test/jdk/com/sun/net/httpserver/Test8a.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -21,15 +21,6 @@ * questions. */ -/** - * @test - * @bug 6270015 - * @library /test/lib - * @build jdk.test.lib.net.SimpleSSLContext jdk.test.lib.net.URIBuilder - * @run main/othervm Test8a - * @run main/othervm -Djava.net.preferIPv6Addresses=true Test8a - * @summary Light weight HTTP server - */ import com.sun.net.httpserver.*; @@ -43,15 +34,18 @@ import jdk.test.lib.net.URIBuilder; /** * Test POST large file via fixed len encoding */ - +/* + * @test + * @bug 6270015 + * @summary Light weight HTTP server + * @library /test/lib + * @build jdk.test.lib.net.SimpleSSLContext jdk.test.lib.net.URIBuilder + * @run main/othervm ${test.main.class} + * @run main/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class} + */ public class Test8a extends Test { public static void main (String[] args) throws Exception { - //Logger log = Logger.getLogger ("com.sun.net.httpserver"); - //ConsoleHandler h = new ConsoleHandler(); - //h.setLevel (Level.INFO); - //log.addHandler (h); - //log.setLevel (Level.INFO); HttpsServer server = null; ExecutorService executor = null; try { diff --git a/test/jdk/com/sun/net/httpserver/TestLogging.java b/test/jdk/com/sun/net/httpserver/TestLogging.java index 8f4fbddcd42..3f201f90dd2 100644 --- a/test/jdk/com/sun/net/httpserver/TestLogging.java +++ b/test/jdk/com/sun/net/httpserver/TestLogging.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2026, 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 @@ -21,16 +21,6 @@ * questions. */ -/* - * @test - * @bug 6422914 - * @library /test/lib - * @build jdk.test.lib.Utils - * jdk.test.lib.net.URIBuilder - * @summary change httpserver exception printouts - * @run main TestLogging - * @run main/othervm -Djava.net.preferIPv6Addresses=true TestLogging - */ import com.sun.net.httpserver.*; @@ -45,27 +35,44 @@ import jdk.test.lib.net.URIBuilder; import static jdk.test.lib.Utils.createTempFileOfSize; +/* + * @test + * @bug 6422914 + * @summary change httpserver exception printouts + * @library /test/lib + * @build jdk.test.lib.Utils + * jdk.test.lib.net.URIBuilder + * @comment We use othervm because this test configures logging handlers + * for the system wide "com.sun.net.httpserver" logger + * @run main/othervm ${test.main.class} + * @run main/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class} + */ public class TestLogging extends Test { private static final String TEMP_FILE_PREFIX = HttpServer.class.getPackageName() + '-' + TestLogging.class.getSimpleName() + '-'; + private static final Logger logger = Logger.getLogger("com.sun.net.httpserver"); + + private static void setupLogging() { + logger.setLevel(Level.ALL); + final Handler handler = new ConsoleHandler(); + handler.setLevel(Level.ALL); + logger.addHandler(handler); + } + public static void main (String[] args) throws Exception { HttpServer s1 = null; ExecutorService executor=null; Path filePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 0xBEEF); try { System.out.print ("Test9: "); + setupLogging(); String root = filePath.getParent().toString(); InetAddress loopback = InetAddress.getLoopbackAddress(); InetSocketAddress addr = new InetSocketAddress(loopback, 0); - Logger logger = Logger.getLogger ("com.sun.net.httpserver"); - logger.setLevel (Level.ALL); - Handler h1 = new ConsoleHandler (); - h1.setLevel (Level.ALL); - logger.addHandler (h1); s1 = HttpServer.create (addr, 0); - logger.info (root); + logger.info(root); HttpHandler h = new FileServerHandler (root); HttpContext c1 = s1.createContext ("/", h); executor = Executors.newCachedThreadPool(); diff --git a/test/jdk/com/sun/net/httpserver/bugs/B6886436.java b/test/jdk/com/sun/net/httpserver/bugs/B6886436.java index 5081a81c4c1..c52bbcbdfce 100644 --- a/test/jdk/com/sun/net/httpserver/bugs/B6886436.java +++ b/test/jdk/com/sun/net/httpserver/bugs/B6886436.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, 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 @@ -21,15 +21,6 @@ * questions. */ -/** - * @test - * @bug 6886436 - * @summary HttpServer should not send a body with 204 response. - * @library /test/lib - * @run main B6886436 - * @run main/othervm -Djava.net.preferIPv6Addresses=true B6886436 - */ - import com.sun.net.httpserver.*; import java.util.*; @@ -40,14 +31,29 @@ import java.net.*; import jdk.test.lib.net.URIBuilder; import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED; +/* + * @test + * @bug 6886436 + * @summary HttpServer should not send a body with 204 response. + * @library /test/lib + * @comment We use othervm because this test configures logging handlers + * for the system wide "com.sun.net.httpserver" logger + * @run main/othervm ${test.main.class} + * @run main/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class} + */ public class B6886436 { + private static final Logger logger = Logger.getLogger ("com.sun.net.httpserver"); + + private static void setupLogging() { + final ConsoleHandler c = new ConsoleHandler(); + c.setLevel(Level.WARNING); + logger.addHandler(c); + logger.setLevel(Level.WARNING); + } + public static void main (String[] args) throws Exception { - Logger logger = Logger.getLogger ("com.sun.net.httpserver"); - ConsoleHandler c = new ConsoleHandler(); - c.setLevel (Level.WARNING); - logger.addHandler (c); - logger.setLevel (Level.WARNING); + setupLogging(); // merely for debugging Handler handler = new Handler(); InetAddress loopback = InetAddress.getLoopbackAddress(); InetSocketAddress addr = new InetSocketAddress (loopback, 0); diff --git a/test/jdk/com/sun/net/httpserver/bugs/B8211420.java b/test/jdk/com/sun/net/httpserver/bugs/B8211420.java index 297aed43bec..d7257ddc755 100644 --- a/test/jdk/com/sun/net/httpserver/bugs/B8211420.java +++ b/test/jdk/com/sun/net/httpserver/bugs/B8211420.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -21,15 +21,6 @@ * questions. */ -/** - * @test - * @bug 8211420 - * @library /test/lib - * @run main/othervm B8211420 - * @run main/othervm -Djava.net.preferIPv6Addresses=true B8211420 - * @summary - */ - import com.sun.net.httpserver.*; import java.util.*; @@ -41,14 +32,29 @@ import java.net.*; import jdk.test.lib.net.URIBuilder; import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY; +/* + * @test + * @bug 8211420 + * @library /test/lib + * @comment We use othervm because this test configures logging handlers + * for the system wide "com.sun.net.httpserver" logger + * @run main/othervm ${test.main.class} + * @run main/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class} + * @summary + */ public class B8211420 { - public static void main(String[] args) throws Exception { - Logger logger = Logger.getLogger("com.sun.net.httpserver"); - ConsoleHandler c = new ConsoleHandler(); + private static final Logger logger = Logger.getLogger("com.sun.net.httpserver"); + + private static void setupLogging() { + final ConsoleHandler c = new ConsoleHandler(); c.setLevel(Level.WARNING); logger.addHandler(c); logger.setLevel(Level.WARNING); + } + + public static void main(String[] args) throws Exception { + setupLogging(); // merely for debugging Handler handler = new Handler(); InetAddress loopback = InetAddress.getLoopbackAddress(); InetSocketAddress addr = new InetSocketAddress(loopback, 0); diff --git a/test/jdk/com/sun/net/httpserver/bugs/ExceptionKeepAlive.java b/test/jdk/com/sun/net/httpserver/bugs/ExceptionKeepAlive.java index 865e3b7509d..eba6aa67a8f 100644 --- a/test/jdk/com/sun/net/httpserver/bugs/ExceptionKeepAlive.java +++ b/test/jdk/com/sun/net/httpserver/bugs/ExceptionKeepAlive.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -21,15 +21,6 @@ * questions. */ -/** - * @test - * @bug 8219083 - * @summary Exceptions thrown from HttpHandler.handle should not close connection - * if response is completed - * @library /test/lib - * @run junit ExceptionKeepAlive - */ - import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; @@ -42,20 +33,35 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.URL; +import java.util.logging.ConsoleHandler; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.logging.SimpleFormatter; -import java.util.logging.StreamHandler; import static org.junit.jupiter.api.Assertions.*; import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY; -public class ExceptionKeepAlive -{ +/* + * @test + * @bug 8219083 + * @summary Exceptions thrown from HttpHandler.handle should not close connection + * if response is completed + * @library /test/lib + * @comment We use othervm because this test configures logging handlers + * for the system wide "com.sun.net.httpserver" logger + * @run junit/othervm ${test.main.class} + */ +public class ExceptionKeepAlive { public static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver"); + private static void setupLogging() { + final Handler handler = new ConsoleHandler(); + handler.setLevel(Level.FINEST); + LOGGER.setLevel(Level.FINEST); + LOGGER.addHandler(handler); + } + @Test void test() throws IOException, InterruptedException { HttpServer httpServer = startHttpServer(); @@ -89,11 +95,7 @@ public class ExceptionKeepAlive * Http Server */ HttpServer startHttpServer() throws IOException { - Handler outHandler = new StreamHandler(System.out, - new SimpleFormatter()); - outHandler.setLevel(Level.FINEST); - LOGGER.setLevel(Level.FINEST); - LOGGER.addHandler(outHandler); + setupLogging(); // merely for debugging InetAddress loopback = InetAddress.getLoopbackAddress(); HttpServer httpServer = HttpServer.create(new InetSocketAddress(loopback, 0), 0); httpServer.createContext("/", new MyHandler()); diff --git a/test/jdk/com/sun/net/httpserver/bugs/FixedLengthInputStream.java b/test/jdk/com/sun/net/httpserver/bugs/FixedLengthInputStream.java index c2195b3568f..c4287f50c9a 100644 --- a/test/jdk/com/sun/net/httpserver/bugs/FixedLengthInputStream.java +++ b/test/jdk/com/sun/net/httpserver/bugs/FixedLengthInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, 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 @@ -21,25 +21,14 @@ * questions. */ -/** - * @test - * @bug 6756771 6755625 - * @summary com.sun.net.httpserver.HttpServer should handle POSTs larger than 2Gig - * @library /test/lib - * @run main FixedLengthInputStream - * @run main/othervm -Djava.net.preferIPv6Addresses=true FixedLengthInputStream - */ - import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; -import java.io.PrintStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.HttpURLConnection; import java.net.Proxy; import java.net.URL; -import java.net.Socket; import java.util.logging.*; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; @@ -47,10 +36,21 @@ import com.sun.net.httpserver.HttpServer; import jdk.test.lib.net.URIBuilder; import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY; -public class FixedLengthInputStream -{ +/* + * @test + * @bug 6756771 6755625 + * @summary com.sun.net.httpserver.HttpServer should handle POSTs larger than 2Gig + * @library /test/lib + * @comment We use othervm because this test configures logging handlers + * for the system wide "com.sun.net.httpserver" logger + * @run main/othervm ${test.main.class} + * @run main/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class} + */ +public class FixedLengthInputStream { static final long POST_SIZE = 4L * 1024L * 1024L * 1024L; // 4Gig + private static final Logger logger = Logger.getLogger("com.sun.net.httpserver"); + void test(String[] args) throws IOException { HttpServer httpServer = startHttpServer(); int port = httpServer.getAddress().getPort(); @@ -89,18 +89,19 @@ public class FixedLengthInputStream } } + private static void setupLogging() { + final ConsoleHandler handler = new ConsoleHandler(); + handler.setLevel(Level.FINEST); + logger.setLevel(Level.FINEST); + logger.addHandler(handler); + } + /** * Http Server */ HttpServer startHttpServer() throws IOException { if (debug) { - Logger logger = - Logger.getLogger("com.sun.net.httpserver"); - Handler outHandler = new StreamHandler(System.out, - new SimpleFormatter()); - outHandler.setLevel(Level.FINEST); - logger.setLevel(Level.FINEST); - logger.addHandler(outHandler); + setupLogging(); // merely for debugging } InetAddress loopback = InetAddress.getLoopbackAddress(); HttpServer httpServer = HttpServer.create(new InetSocketAddress(loopback, 0), 0); diff --git a/test/jdk/com/sun/net/httpserver/bugs/HandlerConnectionClose.java b/test/jdk/com/sun/net/httpserver/bugs/HandlerConnectionClose.java index 372e941904e..215a189c7bf 100644 --- a/test/jdk/com/sun/net/httpserver/bugs/HandlerConnectionClose.java +++ b/test/jdk/com/sun/net/httpserver/bugs/HandlerConnectionClose.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -21,15 +21,6 @@ * questions. */ -/** - * @test - * @bug 8218554 - * @summary test that the handler can request a connection close. - * @library /test/lib - * @build jdk.test.lib.net.SimpleSSLContext - * @run main/othervm HandlerConnectionClose - */ - import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; @@ -49,22 +40,30 @@ import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Locale; +import java.util.logging.ConsoleHandler; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.logging.SimpleFormatter; -import java.util.logging.StreamHandler; import jdk.test.lib.net.SimpleSSLContext; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; -public class HandlerConnectionClose -{ +/* + * @test + * @bug 8218554 + * @summary test that the handler can request a connection close. + * @library /test/lib + * @build jdk.test.lib.net.SimpleSSLContext + * @comment We use othervm because this test configures logging handlers + * for the system wide "com.sun.net.httpserver" logger + * @run main/othervm ${test.main.class} + */ +public class HandlerConnectionClose { static final int ONEK = 1024; static final long POST_SIZE = ONEK * 1L; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - Logger logger; + private static final Logger logger = Logger.getLogger("com.sun.net.httpserver"); void test(String[] args) throws Exception { @@ -341,17 +340,20 @@ public class HandlerConnectionClose } } + private static void setupLogging() { + final Handler handler = new ConsoleHandler(); + handler.setLevel(Level.FINEST); + logger.setLevel(Level.FINEST); + logger.addHandler(handler); + + } + /** * Http Server */ HttpServer startHttpServer(String protocol) throws IOException { if (debug) { - logger = Logger.getLogger("com.sun.net.httpserver"); - Handler outHandler = new StreamHandler(System.out, - new SimpleFormatter()); - outHandler.setLevel(Level.FINEST); - logger.setLevel(Level.FINEST); - logger.addHandler(outHandler); + setupLogging(); // merely for debugging } InetSocketAddress serverAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); diff --git a/test/jdk/com/sun/net/httpserver/bugs/HeadKeepAlive.java b/test/jdk/com/sun/net/httpserver/bugs/HeadKeepAlive.java index 4d987ada173..c575fdfc810 100644 --- a/test/jdk/com/sun/net/httpserver/bugs/HeadKeepAlive.java +++ b/test/jdk/com/sun/net/httpserver/bugs/HeadKeepAlive.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -21,14 +21,6 @@ * questions. */ -/** - * @test - * @bug 8304963 - * @summary Connection should be reusable after HEAD request - * @library /test/lib - * @run junit HeadKeepAlive - */ - import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; @@ -41,19 +33,33 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.URL; +import java.util.logging.ConsoleHandler; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.logging.SimpleFormatter; -import java.util.logging.StreamHandler; import static org.junit.jupiter.api.Assertions.*; import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY; -public class HeadKeepAlive -{ +/* + * @test + * @bug 8304963 + * @summary Connection should be reusable after HEAD request + * @library /test/lib + * @comment We use othervm because this test configures logging handlers + * for the system wide "com.sun.net.httpserver" logger + * @run junit/othervm ${test.main.class} + */ +public class HeadKeepAlive { - public static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver"); + private static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver"); + + private static void setupLogging() { + final Handler handler = new ConsoleHandler(); + handler.setLevel(Level.FINEST); + LOGGER.setLevel(Level.FINEST); + LOGGER.addHandler(handler); + } @Test void test() throws IOException, InterruptedException { @@ -90,11 +96,7 @@ public class HeadKeepAlive * Http Server */ HttpServer startHttpServer() throws IOException { - Handler outHandler = new StreamHandler(System.out, - new SimpleFormatter()); - outHandler.setLevel(Level.FINEST); - LOGGER.setLevel(Level.FINEST); - LOGGER.addHandler(outHandler); + setupLogging(); // merely for debugging InetAddress loopback = InetAddress.getLoopbackAddress(); HttpServer httpServer = HttpServer.create(new InetSocketAddress(loopback, 0), 0); httpServer.createContext("/", new MyHandler()); diff --git a/test/jdk/com/sun/net/httpserver/bugs/TruncatedRequestBody.java b/test/jdk/com/sun/net/httpserver/bugs/TruncatedRequestBody.java index 824428755a4..95a3d1787df 100644 --- a/test/jdk/com/sun/net/httpserver/bugs/TruncatedRequestBody.java +++ b/test/jdk/com/sun/net/httpserver/bugs/TruncatedRequestBody.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -21,11 +21,6 @@ * questions. */ -/** - * @test - * @bug 8190793 - * @summary Httpserver does not detect truncated request body - */ import com.sun.net.httpserver.HttpContext; import com.sun.net.httpserver.HttpExchange; @@ -46,12 +41,23 @@ import java.util.logging.Level; import java.util.logging.Logger; import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY; +/* + * @test + * @bug 8190793 + * @summary Httpserver does not detect truncated request body + * @comment We use othervm because this test configures logging handlers + * for the system wide "com.sun.net.httpserver" logger + * @run main/othervm ${test.main.class} + */ /* * Send two POST requests to the server which are both trucated * and socket closed. Server needs to detect this and throw an IOException * in getRequestBody().read(). Two variants for fixed length and chunked. */ public class TruncatedRequestBody { + + private static final Logger logger = Logger.getLogger("com.sun.net.httpserver"); + static volatile boolean error = false; static CountDownLatch latch = new CountDownLatch(2); @@ -81,16 +87,18 @@ public class TruncatedRequestBody { } + private static void setupLogging() { + final ConsoleHandler h = new ConsoleHandler(); + h.setLevel(Level.ALL); + logger.setLevel(Level.ALL); + logger.addHandler(h); + } + /** * @param args the command line arguments */ public static void main(String[] args) throws IOException, InterruptedException { - Logger logger = Logger.getLogger("com.sun.net.httpserver"); - ConsoleHandler h = new ConsoleHandler(); - h.setLevel(Level.ALL); - logger.setLevel(Level.ALL); - logger.addHandler(h); - + setupLogging(); // merely for debugging InetAddress loopback = InetAddress.getLoopbackAddress(); InetSocketAddress addr = new InetSocketAddress(loopback, 0); HttpServer server = HttpServer.create(addr, 10); diff --git a/test/jdk/com/sun/net/httpserver/bugs/ZeroLengthOutputStream.java b/test/jdk/com/sun/net/httpserver/bugs/ZeroLengthOutputStream.java index 33bbe0479db..0715800ead7 100644 --- a/test/jdk/com/sun/net/httpserver/bugs/ZeroLengthOutputStream.java +++ b/test/jdk/com/sun/net/httpserver/bugs/ZeroLengthOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -21,14 +21,6 @@ * questions. */ -/** - * @test - * @bug 8219083 - * @summary HttpExchange.getResponseBody write and close should not throw - * even when response length is zero - * @library /test/lib - * @run junit ZeroLengthOutputStream - */ import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; @@ -44,19 +36,27 @@ import java.net.InetSocketAddress; import java.net.Proxy; import java.net.URL; import java.util.concurrent.CountDownLatch; +import java.util.logging.ConsoleHandler; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.logging.SimpleFormatter; -import java.util.logging.StreamHandler; import static org.junit.jupiter.api.Assertions.*; import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY; -public class ZeroLengthOutputStream -{ +/* + * @test + * @bug 8219083 + * @summary HttpExchange.getResponseBody write and close should not throw + * even when response length is zero + * @library /test/lib + * @comment We use othervm because this test configures logging handlers + * for the system wide "com.sun.net.httpserver" logger + * @run junit/othervm ${test.main.class} + */ +public class ZeroLengthOutputStream { - public static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver"); + private static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver"); public volatile boolean closed; public CountDownLatch cdl = new CountDownLatch(1); @@ -80,15 +80,18 @@ public class ZeroLengthOutputStream } } + private static void setupLogging() { + final Handler handler = new ConsoleHandler(); + handler.setLevel(Level.FINEST); + LOGGER.setLevel(Level.FINEST); + LOGGER.addHandler(handler); + } + /** * Http Server */ HttpServer startHttpServer() throws IOException { - Handler outHandler = new StreamHandler(System.out, - new SimpleFormatter()); - outHandler.setLevel(Level.FINEST); - LOGGER.setLevel(Level.FINEST); - LOGGER.addHandler(outHandler); + setupLogging(); // merely for debugging InetAddress loopback = InetAddress.getLoopbackAddress(); HttpServer httpServer = HttpServer.create(new InetSocketAddress(loopback, 0), 0); httpServer.createContext("/flis/", new MyHandler()); diff --git a/test/jdk/com/sun/nio/sctp/SctpChannel/Bind.java b/test/jdk/com/sun/nio/sctp/SctpChannel/Bind.java index ff5ca00846c..a1eb2233b02 100644 --- a/test/jdk/com/sun/nio/sctp/SctpChannel/Bind.java +++ b/test/jdk/com/sun/nio/sctp/SctpChannel/Bind.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, 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 @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpChannel/CloseDescriptors.java b/test/jdk/com/sun/nio/sctp/SctpChannel/CloseDescriptors.java index 5d62b88f23e..bf5872b06d3 100644 --- a/test/jdk/com/sun/nio/sctp/SctpChannel/CloseDescriptors.java +++ b/test/jdk/com/sun/nio/sctp/SctpChannel/CloseDescriptors.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 diff --git a/test/jdk/com/sun/nio/sctp/SctpChannel/CommUp.java b/test/jdk/com/sun/nio/sctp/SctpChannel/CommUp.java index 320e556f800..67a946c15d0 100644 --- a/test/jdk/com/sun/nio/sctp/SctpChannel/CommUp.java +++ b/test/jdk/com/sun/nio/sctp/SctpChannel/CommUp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, 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 @@ -23,6 +23,7 @@ /* @test * @bug 6863110 + * @requires (os.family == "linux") * @library /test/lib * @summary Newly connected/accepted SctpChannel should fire OP_READ if registered with a Selector * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpChannel/Connect.java b/test/jdk/com/sun/nio/sctp/SctpChannel/Connect.java index 79b9453a595..699ef3d0971 100644 --- a/test/jdk/com/sun/nio/sctp/SctpChannel/Connect.java +++ b/test/jdk/com/sun/nio/sctp/SctpChannel/Connect.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, 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 @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpChannel/Receive.java b/test/jdk/com/sun/nio/sctp/SctpChannel/Receive.java index 48eb413a9ca..896c339dcc0 100644 --- a/test/jdk/com/sun/nio/sctp/SctpChannel/Receive.java +++ b/test/jdk/com/sun/nio/sctp/SctpChannel/Receive.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, 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 @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpChannel/ReceiveIntoDirect.java b/test/jdk/com/sun/nio/sctp/SctpChannel/ReceiveIntoDirect.java index 48d89c34ca4..1c52f83b972 100644 --- a/test/jdk/com/sun/nio/sctp/SctpChannel/ReceiveIntoDirect.java +++ b/test/jdk/com/sun/nio/sctp/SctpChannel/ReceiveIntoDirect.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -23,6 +23,7 @@ /* @test * @bug 8034181 + * @requires (os.family == "linux") * @library /test/lib * @summary SIGBUS in SctpChannelImpl receive * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpChannel/Send.java b/test/jdk/com/sun/nio/sctp/SctpChannel/Send.java index 35a28f4eb38..a04205a9463 100644 --- a/test/jdk/com/sun/nio/sctp/SctpChannel/Send.java +++ b/test/jdk/com/sun/nio/sctp/SctpChannel/Send.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, 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 @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpChannel/Shutdown.java b/test/jdk/com/sun/nio/sctp/SctpChannel/Shutdown.java index 0891851fe86..75f0fa9a724 100644 --- a/test/jdk/com/sun/nio/sctp/SctpChannel/Shutdown.java +++ b/test/jdk/com/sun/nio/sctp/SctpChannel/Shutdown.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, 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 @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java b/test/jdk/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java index 857efbb63f5..2e6192a0d0e 100644 --- a/test/jdk/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java +++ b/test/jdk/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, 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 @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/Branch.java b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/Branch.java index 9beb8c74c3e..199cbfc0fc1 100644 --- a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/Branch.java +++ b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/Branch.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, 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 @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/CloseDescriptors.java b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/CloseDescriptors.java index fdafec80745..f6fffe700c5 100644 --- a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/CloseDescriptors.java +++ b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/CloseDescriptors.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 diff --git a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/Send.java b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/Send.java index 8fb551dd8f4..6f1b05e9db9 100644 --- a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/Send.java +++ b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/Send.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, 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 @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/SendFailed.java b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/SendFailed.java index a64d4d58270..85db90279c0 100644 --- a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/SendFailed.java +++ b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/SendFailed.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -23,6 +23,7 @@ /* @test * @bug 8067846 + * @requires (os.family == "linux") * @library /test/lib * @summary Test for send failed notification */ diff --git a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/SocketOptionTests.java b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/SocketOptionTests.java index 3a681884b9a..5d782fcd4e7 100644 --- a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/SocketOptionTests.java +++ b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/SocketOptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, 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 @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpServerChannel/Accept.java b/test/jdk/com/sun/nio/sctp/SctpServerChannel/Accept.java index c68bfd28945..84c010aaaf0 100644 --- a/test/jdk/com/sun/nio/sctp/SctpServerChannel/Accept.java +++ b/test/jdk/com/sun/nio/sctp/SctpServerChannel/Accept.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, 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 @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpServerChannel/NonBlockingAccept.java b/test/jdk/com/sun/nio/sctp/SctpServerChannel/NonBlockingAccept.java index abf35bf779d..3c358e0a86d 100644 --- a/test/jdk/com/sun/nio/sctp/SctpServerChannel/NonBlockingAccept.java +++ b/test/jdk/com/sun/nio/sctp/SctpServerChannel/NonBlockingAccept.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, 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 @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/java/awt/Clipboard/FlavorChangeNotificationTest/SystemClipboardTest.java b/test/jdk/java/awt/Clipboard/FlavorChangeNotificationTest/SystemClipboardTest.java deleted file mode 100644 index ff19005b8aa..00000000000 --- a/test/jdk/java/awt/Clipboard/FlavorChangeNotificationTest/SystemClipboardTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2003, 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 4259272 - @summary tests that notifications on changes to the set of DataFlavors - available on the system clipboard are delivered properly - @key headful - @modules java.desktop/sun.awt - @build Common - @run main SystemClipboardTest -*/ - -import java.awt.Toolkit; -import java.awt.datatransfer.Clipboard; -import java.awt.datatransfer.StringSelection; - -import sun.awt.SunToolkit; - -public class SystemClipboardTest { - - private final Clipboard clipboard = - Toolkit.getDefaultToolkit().getSystemClipboard(); - - private final FlavorListenerImpl listener1 = new FlavorListenerImpl(); - - private final FlavorListenerImpl listener2 = new FlavorListenerImpl(); - - private boolean isListener2Added; - - - public static void main(String[] args) { - new SystemClipboardTest().start(); - } - - public void start() { - Util.setClipboardContents(clipboard, - new StringSelection("text3"), null); - - clipboard.addFlavorListener(listener1); - - final ThreadGroup threadGroup = new ThreadGroup("Test thread group"); - final Object lock = new Object(); - final Runnable runnable = new Runnable() { - public void run() { - SunToolkit.createNewAppContext(); - clipboard.addFlavorListener(listener2); - synchronized (lock) { - isListener2Added = true; - lock.notifyAll(); - } - } - }; - final Thread thread = new Thread(threadGroup, runnable, "Test thread"); - synchronized (lock) { - thread.start(); - while (!isListener2Added) { - try { - lock.wait(); - } catch (InterruptedException ie) { - ie.printStackTrace(); - } - } - } - - Util.setClipboardContents(clipboard, - new TransferableUnion(new StringSelection("text2"), - new ImageSelection(Util.createImage())), - null); - Util.sleep(3000); - - clipboard.removeFlavorListener(listener1); - // must not remove listener2 from this AppContext - - Util.setClipboardContents(clipboard, - new StringSelection("text3"), null); - Util.sleep(3000); - - System.err.println("listener1: " + listener1 - + "\nlistener2: " + listener2); - - if (!(listener1.notified1 - && listener2.notified1 - && !listener1.notified2 - && listener2.notified2)) { - throw new RuntimeException("notifications about flavor " + - "changes delivered incorrectly!"); - } - } -} \ No newline at end of file diff --git a/test/jdk/java/awt/Focus/NullActiveWindowOnFocusLost/NullActiveWindowOnFocusLost.java b/test/jdk/java/awt/Focus/NullActiveWindowOnFocusLost/NullActiveWindowOnFocusLost.java deleted file mode 100644 index 0924413bec4..00000000000 --- a/test/jdk/java/awt/Focus/NullActiveWindowOnFocusLost/NullActiveWindowOnFocusLost.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2018, 2019, 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.awt.Button; -import java.awt.Frame; -import java.util.concurrent.TimeUnit; - -import sun.awt.SunToolkit; - -/** - * @test - * @key headful - * @bug 8211435 - * @modules java.desktop/sun.awt - */ -public final class NullActiveWindowOnFocusLost { - - private static volatile long endtime; - private static Throwable failed; - - public static void main(final String[] args) throws Exception { - // Will run the test no more than 30 seconds - endtime = System.nanoTime() + TimeUnit.SECONDS.toNanos(30); - Thread.setDefaultUncaughtExceptionHandler((t, e) -> failed = e); - - final Thread[] threads = new Thread[20]; - for (int i = 0; i < threads.length; i++) { - threads[i] = testThread(i); - } - for (final Thread thread : threads) { - thread.start(); - } - for (final Thread thread : threads) { - thread.join(); - } - if (failed != null) { - failed.printStackTrace(); - throw new RuntimeException(failed); - } - } - - private static Thread testThread(int index) { - return new Thread(new ThreadGroup("TG " + index), () -> { - SunToolkit.createNewAppContext(); - while (!isComplete()) { - final Frame frame = new Frame(); - frame.setSize(300, 300); - frame.add(new Button("Button")); - frame.setLocationRelativeTo(null); - frame.setVisible(true); - try { - Thread.sleep(index); // increase probability of the failure - } catch (InterruptedException ignored) { - } - frame.dispose(); - } - }); - } - - private static boolean isComplete() { - return endtime - System.nanoTime() < 0 || failed != null; - } -} diff --git a/test/jdk/java/awt/SplashScreen/MultiResolutionSplash/MultiResolutionSplashTest.java b/test/jdk/java/awt/SplashScreen/MultiResolutionSplash/MultiResolutionSplashTest.java index 1f2de081cce..c0a9c90feba 100644 --- a/test/jdk/java/awt/SplashScreen/MultiResolutionSplash/MultiResolutionSplashTest.java +++ b/test/jdk/java/awt/SplashScreen/MultiResolutionSplash/MultiResolutionSplashTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -22,30 +22,25 @@ */ import java.awt.Color; -import java.awt.Dialog; import java.awt.Frame; import java.awt.Graphics; -import java.awt.Graphics2D; import java.awt.GraphicsEnvironment; -import java.awt.Panel; import java.awt.Rectangle; import java.awt.Robot; import java.awt.SplashScreen; import java.awt.TextField; -import java.awt.Window; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.io.File; +import java.io.IOException; + import javax.imageio.ImageIO; -import sun.java2d.SunGraphics2D; - -/** +/* * @test * @key headful * @bug 8043869 8075244 8078082 8145173 8151787 8212213 * @summary Tests the HiDPI splash screen support for windows and MAC - * @modules java.desktop/sun.java2d * @run main MultiResolutionSplashTest GENERATE_IMAGES * @run main/othervm -splash:splash1.png MultiResolutionSplashTest TEST_SPLASH 0 * @run main/othervm -splash:splash2 MultiResolutionSplashTest TEST_SPLASH 1 @@ -56,47 +51,54 @@ public class MultiResolutionSplashTest { private static final int IMAGE_WIDTH = 300; private static final int IMAGE_HEIGHT = 200; - private static boolean isMac; - static { - isMac = System.getProperty("os.name").contains("OS X"); - } + private static final boolean isMac = System.getProperty("os.name") + .contains("OS X"); + private static final ImageInfo[] tests = { - new ImageInfo("splash1.png", "splash1@2x.png", Color.BLUE, Color.GREEN), - new ImageInfo("splash2", "splash2@2x", Color.WHITE, Color.BLACK), - new ImageInfo("splash3.", "splash3@2x.", Color.YELLOW, Color.RED) + new ImageInfo("splash1", ".png", Color.BLUE, Color.GREEN), + new ImageInfo("splash2", "", Color.WHITE, Color.BLACK), + new ImageInfo("splash3", ".", Color.YELLOW, Color.RED) }; public static void main(String[] args) throws Exception { - - String test = args[0]; - switch (test) { + switch (args[0]) { case "GENERATE_IMAGES": generateImages(); break; case "TEST_SPLASH": - int index = Integer.parseInt(args[1]); - testSplash(tests[index]); + testSplash(tests[Integer.parseInt(args[1])]); break; case "TEST_FOCUS": testFocus(); break; default: - throw new RuntimeException("Unknown test: " + test); + throw new RuntimeException("Unknown test: " + args[0]); } } static void testSplash(ImageInfo test) throws Exception { SplashScreen splashScreen = SplashScreen.getSplashScreen(); - if (splashScreen == null) { throw new RuntimeException("Splash screen is not shown!"); } - Graphics2D g = splashScreen.createGraphics(); - Rectangle splashBounds = splashScreen.getBounds(); - int screenX = (int) splashBounds.getCenterX(); - int screenY = (int) splashBounds.getCenterY(); + final Rectangle splashBounds = splashScreen.getBounds(); + final double scaleFactor = getScreenScaleFactor(); + + final Robot robot = new Robot(); + // Allow time for the splash screen to show + robot.delay(100); + + BufferedImage splashCapture = robot.createScreenCapture(splashBounds); + String captureFileName = "splashscreen-%1.2f-%s.png" + .formatted(scaleFactor, test.name1x); + saveImageNoError(splashCapture, new File(captureFileName)); + + // Close the splash screen; this gives time for it to be fully removed + splashScreen.close(); + robot.waitForIdle(); + if (splashBounds.width != IMAGE_WIDTH) { throw new RuntimeException( "SplashScreen#getBounds has wrong width"); @@ -106,19 +108,19 @@ public class MultiResolutionSplashTest { "SplashScreen#getBounds has wrong height"); } - Robot robot = new Robot(); - Color splashScreenColor = robot.getPixelColor(screenX, screenY); - float scaleFactor = getScaleFactor(); + Color splashScreenColor = + new Color(splashCapture.getRGB(splashBounds.width / 2, + splashBounds.height / 2)); Color testColor = (1 < scaleFactor) ? test.color2x : test.color1x; if (!compare(testColor, splashScreenColor)) { throw new RuntimeException( - "Image with wrong resolution is used for splash screen!"); + "Image with wrong resolution is used for splash screen! " + + "Refer to " + captureFileName); } } static void testFocus() throws Exception { - Robot robot = new Robot(); robot.setAutoWaitForIdle(true); robot.setAutoDelay(50); @@ -157,94 +159,85 @@ public class MultiResolutionSplashTest { return Math.abs(n - m) <= 50; } - static float getScaleFactor() { - - final Dialog dialog = new Dialog((Window) null); - dialog.setSize(100, 100); - dialog.setModal(true); - final float[] scaleFactors = new float[1]; - Panel panel = new Panel() { - - @Override - public void paint(Graphics g) { - float scaleFactor = 1; - if (g instanceof SunGraphics2D) { - scaleFactor = getScreenScaleFactor(); - } - scaleFactors[0] = scaleFactor; - dialog.setVisible(false); - } - }; - - dialog.add(panel); - dialog.setVisible(true); - dialog.dispose(); - - return scaleFactors[0]; - } - static void generateImages() throws Exception { for (ImageInfo test : tests) { - generateImage(test.name1x, test.color1x, 1); - generateImage(test.name2x, test.color2x, getScreenScaleFactor()); + generateImage(test.name1x, test.color1x, 1.0); + + // Ensure the second image uses scale greater than 1.0 + double scale = getAdjustedScaleFactor(); + generateImage(test.name2x, test.color2x, scale); } } - static void generateImage(String name, Color color, float scale) throws Exception { + static void generateImage(final String name, + final Color color, + final double scale) throws Exception { File file = new File(name); if (file.exists()) { return; } - BufferedImage image = new BufferedImage((int) (scale * IMAGE_WIDTH), - (int) (scale * IMAGE_HEIGHT), BufferedImage.TYPE_INT_RGB); + + final int width = (int) (scale * IMAGE_WIDTH); + final int height = (int) (scale * IMAGE_HEIGHT); + BufferedImage image = new BufferedImage(width, + height, + BufferedImage.TYPE_INT_RGB); Graphics g = image.getGraphics(); g.setColor(color); - g.fillRect(0, 0, (int) (scale * IMAGE_WIDTH), (int) (scale * IMAGE_HEIGHT)); + g.fillRect(0, 0, width, height); + + saveImage(image, file); + } + + private static void saveImage(BufferedImage image, + File file) throws IOException { ImageIO.write(image, "png", file); } - static float getScreenScaleFactor() { - return (float) GraphicsEnvironment. - getLocalGraphicsEnvironment(). - getDefaultScreenDevice().getDefaultConfiguration(). - getDefaultTransform().getScaleX(); + private static void saveImageNoError(BufferedImage image, + File file) { + try { + saveImage(image, file); + } catch (IOException ignored) { + } + } + + static double getScreenScaleFactor() { + return GraphicsEnvironment + .getLocalGraphicsEnvironment() + .getDefaultScreenDevice() + .getDefaultConfiguration() + .getDefaultTransform() + .getScaleX(); + } + + // Ensure the second image uses scale greater than 1.0 + static double getAdjustedScaleFactor() { + double scale = getScreenScaleFactor(); + return scale < 1.25 ? 2.0 : scale; } static class ImageInfo { - final String name1x; final String name2x; final Color color1x; final Color color2x; - public ImageInfo(String name1x, String name2x, Color color1x, Color color2x) { - this.name1x = name1x; - if (!isMac) { - float scale = getScreenScaleFactor(); - StringBuffer buff = new StringBuffer(); - if (scale - (int) scale > 0) { - buff.append("@").append((int) (scale * 100)).append("pct"); - } else { - buff.append("@").append((int) scale).append("x"); - } - StringBuffer buffer = new StringBuffer(); - String[] splitStr = name1x.split("\\."); - if (splitStr.length == 2) { - this.name2x = buffer.append(splitStr[0]).append(buff) - .append(".").append(splitStr[1]).toString(); - } else { - if (name1x.indexOf(".") > 0) { - this.name2x = buffer.append(splitStr[0]).append(buff).append(".").toString(); - } else { - this.name2x = buffer.append(splitStr[0]).append(buff).toString(); - } - } - } else { - this.name2x = name2x; - } + public ImageInfo(String baseName, String ext, + Color color1x, Color color2x) { + this.name1x = baseName + ext; + this.name2x = createName2x(baseName, ext); this.color1x = color1x; this.color2x = color2x; } + + private static String createName2x(String baseName, String ext) { + double scale = getAdjustedScaleFactor(); + if (!isMac && (((int) (scale * 100)) % 100 != 0)) { + return baseName + "@" + ((int) (scale * 100)) + "pct" + ext; + } else { + return baseName + "@" + ((int) scale) + "x" + ext; + } + } } } - diff --git a/test/jdk/java/awt/TextComponent/CorrectTextComponentSelectionTest.java b/test/jdk/java/awt/TextComponent/CorrectTextComponentSelectionTest.java index 9d3e6d85637..b2352dc04ff 100644 --- a/test/jdk/java/awt/TextComponent/CorrectTextComponentSelectionTest.java +++ b/test/jdk/java/awt/TextComponent/CorrectTextComponentSelectionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -23,9 +23,9 @@ /* * @test + * @key headful * @bug 5100806 * @summary TextArea.select(0,0) does not de-select the selected text properly - * @key headful * @run main CorrectTextComponentSelectionTest */ @@ -38,7 +38,6 @@ import java.awt.Robot; import java.awt.TextArea; import java.awt.TextComponent; import java.awt.TextField; -import java.lang.reflect.InvocationTargetException; public class CorrectTextComponentSelectionTest { static TextField tf = new TextField("TextField"); @@ -46,14 +45,11 @@ public class CorrectTextComponentSelectionTest { static Robot r; static Frame frame; static volatile Color color_center; - static volatile Point loc; public static void main(String[] args) throws Exception { try { r = new Robot(); - EventQueue.invokeAndWait(() -> { - initialize(); - }); + EventQueue.invokeAndWait(() -> initialize()); r.waitForIdle(); r.delay(1000); @@ -75,24 +71,13 @@ public class CorrectTextComponentSelectionTest { // We should place to the text components the long strings in order to // cover the components by the selection completely - String sf = ""; - for (int i = 0; i < 50; i++) { - sf = sf + " "; - } - tf.setText(sf); + tf.setText(" ".repeat(50)); // We check the color of the text component in order to find out the // bug reproducible situation tf.setForeground(Color.WHITE); tf.setBackground(Color.WHITE); - String sa = ""; - for (int i = 0; i < 50; i++) { - for (int j = 0; j < 50; j++) { - sa = sa + " "; - } - sa = sa + "\n"; - } - ta.setText(sa); + ta.setText((" ".repeat(50) + "\n").repeat(50)); ta.setForeground(Color.WHITE); ta.setBackground(Color.WHITE); @@ -111,26 +96,24 @@ public class CorrectTextComponentSelectionTest { r.waitForIdle(); r.delay(100); + EventQueue.invokeAndWait(() -> { tc.requestFocus(); tc.selectAll(); tc.select(0, 0); }); - r.waitForIdle(); - r.delay(100); - EventQueue.invokeAndWait(() -> { - loc = tc.getLocationOnScreen(); - }); r.waitForIdle(); r.delay(100); EventQueue.invokeAndWait(() -> { - color_center = r.getPixelColor(loc.x + tc.getWidth() / 2, loc.y + tc.getHeight() / 2); + Point p = tc.getLocationOnScreen(); + p.translate(tc.getWidth() / 2, tc.getHeight() / 2); + color_center = r.getPixelColor(p.x, p.y); }); - System.out.println("Color of the text component (CENTER) =" + color_center); - System.out.println("White color=" + Color.WHITE); + System.out.println("Color of the text component (CENTER) = " + color_center); + System.out.println("White color = " + Color.WHITE); if (color_center.getRGB() != Color.WHITE.getRGB()) { throw new RuntimeException("Test Failed"); diff --git a/test/jdk/java/awt/dnd/BadSerializationTest/badAction b/test/jdk/java/awt/dnd/BadSerializationTest/badAction index c02632cabfd..4556c2e5b1f 100644 Binary files a/test/jdk/java/awt/dnd/BadSerializationTest/badAction and b/test/jdk/java/awt/dnd/BadSerializationTest/badAction differ diff --git a/test/jdk/java/awt/dnd/BadSerializationTest/good b/test/jdk/java/awt/dnd/BadSerializationTest/good index 523df353a1a..f5dd0e2cb5b 100644 Binary files a/test/jdk/java/awt/dnd/BadSerializationTest/good and b/test/jdk/java/awt/dnd/BadSerializationTest/good differ diff --git a/test/jdk/java/awt/dnd/BadSerializationTest/noEvents b/test/jdk/java/awt/dnd/BadSerializationTest/noEvents index a45ba168c71..fdf1125889f 100644 Binary files a/test/jdk/java/awt/dnd/BadSerializationTest/noEvents and b/test/jdk/java/awt/dnd/BadSerializationTest/noEvents differ diff --git a/test/jdk/java/awt/dnd/BadSerializationTest/nullComponent b/test/jdk/java/awt/dnd/BadSerializationTest/nullComponent index 63aee01cc2c..d5dfcada90c 100644 Binary files a/test/jdk/java/awt/dnd/BadSerializationTest/nullComponent and b/test/jdk/java/awt/dnd/BadSerializationTest/nullComponent differ diff --git a/test/jdk/java/awt/dnd/BadSerializationTest/nullDragSource b/test/jdk/java/awt/dnd/BadSerializationTest/nullDragSource index a483f7106a8..efdfeb3ed20 100644 Binary files a/test/jdk/java/awt/dnd/BadSerializationTest/nullDragSource and b/test/jdk/java/awt/dnd/BadSerializationTest/nullDragSource differ diff --git a/test/jdk/java/awt/dnd/BadSerializationTest/nullOrigin b/test/jdk/java/awt/dnd/BadSerializationTest/nullOrigin index 5af1996e74b..17d9fd3a1a9 100644 Binary files a/test/jdk/java/awt/dnd/BadSerializationTest/nullOrigin and b/test/jdk/java/awt/dnd/BadSerializationTest/nullOrigin differ diff --git a/test/jdk/java/awt/event/MouseEvent/AltGraphModifierTest/AltGraphModifierTest.java b/test/jdk/java/awt/event/MouseEvent/AltGraphModifierTest/AltGraphModifierTest.java index c8730b28964..615e2cdd6af 100644 --- a/test/jdk/java/awt/event/MouseEvent/AltGraphModifierTest/AltGraphModifierTest.java +++ b/test/jdk/java/awt/event/MouseEvent/AltGraphModifierTest/AltGraphModifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -23,7 +23,7 @@ /* * @test - * @bug 8041928 8158616 + * @bug 8041928 8158616 8376050 * @requires (os.family != "mac") * @summary Confirm that the Alt-Gr Modifier bit is set correctly. * @library /java/awt/regtesthelpers diff --git a/test/jdk/java/awt/event/SequencedEvent/MultipleContextsFunctionalTest.java b/test/jdk/java/awt/event/SequencedEvent/MultipleContextsFunctionalTest.java deleted file mode 100644 index 9af56ba044b..00000000000 --- a/test/jdk/java/awt/event/SequencedEvent/MultipleContextsFunctionalTest.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (c) 2018, 2022, 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 8204142 - * @key headful - * @summary Deadlock when queueing SequencedEvent of different AppContexts - * @author Laurent Bourges - * @modules java.desktop/sun.awt - * @run main/othervm/timeout=30 MultipleContextsFunctionalTest - */ - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicReference; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.SwingUtilities; -import javax.swing.Timer; - -public final class MultipleContextsFunctionalTest { - - private static final long serialVersionUID = 1L; - - private static final int NUM_WINDOW = 2; - private static final int INTERVAL = 50; - private static final int MAX_TIME = 10000; // 10s - private static final int TOLERANCE = 10000;// 10s - private static final int CHECK_LAPSE = 100; - private static final int MAX_COUNT = MAX_TIME / INTERVAL; - private static final int EXPECTED = MAX_COUNT * NUM_WINDOW; - private static final List WINDOWS = new CopyOnWriteArrayList<>(); - - public static void main(String[] args) { - for (int i = 0; i < NUM_WINDOW; i++) { - createWin(i); - } - - int total = 0; - int waitingTime = MAX_TIME + TOLERANCE; - while (waitingTime > 0 && total != EXPECTED) { - try { - Thread.sleep(CHECK_LAPSE); - } catch (InterruptedException e) { - e.printStackTrace(); - } - waitingTime -= CHECK_LAPSE; - - total = 0; - for (TestWindow window : WINDOWS) { - total += window.getCounter(); - } - } - - // Failure if AWT hanging: assert - System.out.println("Total [" + total + "] - Expected [" + EXPECTED + "]"); - if (total == EXPECTED) { - System.out.println("Test PASSED"); - return; - } - System.out.println("Test FAILED"); - Runtime.getRuntime().halt(-1); - } - - private static void createWin(int tgNum) { - new Thread(new ThreadGroup("TG " + tgNum), - new Runnable() { - @Override - public void run() { - sun.awt.SunToolkit.createNewAppContext(); - - final AtomicReference ref = - new AtomicReference(); - - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - final TestWindow window = new TestWindow(tgNum); - window.setVisible(true); - ref.set(window); - WINDOWS.add(window); - } - }); - - // Wait for window to show - TestWindow window = ref.get(); - while (window == null) { - try { - Thread.sleep(100); - } catch (InterruptedException ie) { - ie.printStackTrace(); - } - window = ref.get(); - } - window.enableTimer(true); - } - }).start(); - } - - private static final class TestWindow extends JFrame implements ActionListener { - - private final JButton btn; - private volatile int counter = 0; - private final Timer t; - - TestWindow(final int num) { - super("Test Window [" + num + "]"); - setMinimumSize(new Dimension(300, 200)); - setLocation(100 + 400 * (num - 1), 100); - - setLayout(new BorderLayout()); - JLabel textBlock = new JLabel("Lorem ipsum dolor sit amet..."); - add(textBlock); - - btn = new JButton("TEST"); - add(btn, BorderLayout.SOUTH); - - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - pack(); - - t = new Timer(INTERVAL, this); - t.setRepeats(false); - } - - @Override - public void actionPerformed(ActionEvent e) { - this.toFront(); - btn.setText("TEST " + (++counter)); - this.toBack(); - if (counter < MAX_COUNT) { - enableTimer(true); - } else { - dispose(); - } - } - - void enableTimer(boolean enable) { - if (enable) { - t.start(); - } else { - t.stop(); - } - } - - int getCounter() { - return counter; - } - } -} diff --git a/test/jdk/java/awt/event/SequencedEvent/MultipleContextsUnitTest.java b/test/jdk/java/awt/event/SequencedEvent/MultipleContextsUnitTest.java deleted file mode 100644 index 8c7062f4abf..00000000000 --- a/test/jdk/java/awt/event/SequencedEvent/MultipleContextsUnitTest.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2018, 2020, 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.awt.AWTEvent; -import java.awt.event.InvocationEvent; -import java.lang.reflect.Constructor; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicReference; - -import sun.awt.AppContext; -import sun.awt.SunToolkit; - -/** - * @test - * @bug 8204142 - * @author Sergey Bylokhov - * @key headful - * @modules java.desktop/java.awt:open java.desktop/sun.awt - * @run main/othervm/timeout=30 MultipleContextsUnitTest - */ -public final class MultipleContextsUnitTest { - - private static final int COUNT = 20; - private static final AppContext[] apps = new AppContext[COUNT]; - private static final CountDownLatch go = new CountDownLatch(1); - private static final CountDownLatch end = new CountDownLatch(COUNT); - - private static volatile int createSENumber = 0; - private static volatile int dispatchSENumber = 0; - - public static void main(final String[] args) throws Exception { - for (int i = 0; i < COUNT; i++) { - Thread t = testThread(i); - t.start(); - t.join(); - } - - for (AppContext app : apps) { - SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> { - try { - go.await(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - })); - } - - // eventOne - created first, but posted last - AWTEvent eventOne = getSequencedEvent(); - { - // eventTwo and eventThree - posted in the reverse order - AppContext app = apps[1]; - AWTEvent eventTwo = getSequencedEvent(); - AWTEvent eventThree = getSequencedEvent(); - SunToolkit.postEvent(app, eventThree); - SunToolkit.postEvent(app, eventTwo); - SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> { - System.err.println(AppContext.getAppContext()); - end.countDown(); - })); - } - - for (int i = 2; i < apps.length; i++) { - // eventTwo and eventThree - posted in the correct order - AppContext app = apps[i]; - - AWTEvent eventTwo = getSequencedEvent(); - SunToolkit.postEvent(app, eventTwo); - - AtomicReference called1 = new AtomicReference(false); - AtomicReference called2 = new AtomicReference(false); - int num1 = createSENumber; - SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> { - if (dispatchSENumber < num1) { - throw new RuntimeException("Dispatched too early"); - } - called1.set(true); - if (called2.get()) { - throw new RuntimeException("Second event is called before first"); - } - })); - AWTEvent eventThree = getSequencedEvent(); - SunToolkit.postEvent(app, eventThree); - int num2 = createSENumber; - SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> { - if (dispatchSENumber < num2) { - throw new RuntimeException("Dispatched too early"); - } - called2.set(true); - if (!called1.get()) { - throw new RuntimeException("First event is not called before second"); - } - System.err.println(AppContext.getAppContext()); - end.countDown(); - })); - } - - - - // eventOne should flush all EDT - SunToolkit.postEvent(apps[0], eventOne); - SunToolkit.postEvent(apps[0], new InvocationEvent(new Object(), () -> { - System.err.println(AppContext.getAppContext()); - end.countDown(); - })); - - go.countDown(); - - System.err.println("Start to wait"); - end.await(); - System.err.println("End to wait"); - } - - private static Thread testThread(int index) { - final ThreadGroup group = new ThreadGroup("TG " + index); - return new Thread(group, () -> { - apps[index] = SunToolkit.createNewAppContext(); - }); - } - - private static AWTEvent getSequencedEvent() - { - int num = createSENumber++; - - InvocationEvent wrapMe = new InvocationEvent(new Object(), () -> { - if (num != dispatchSENumber++) { - System.err.println("num: " + num); - System.err.println("dispatchSENumber: " + dispatchSENumber); - throw new RuntimeException("Wrong order"); - } - }); - - try { - /* - * SequencedEvent is a package private class, which cannot be instantiated - * by importing. So use reflection to create an instance. - */ - Class seqClass = (Class) Class.forName("java.awt.SequencedEvent"); - Constructor - seqConst = seqClass.getConstructor(AWTEvent.class); - seqConst.setAccessible(true); - return seqConst.newInstance(wrapMe); - } catch (Throwable err) { - throw new RuntimeException("Unable to instantiate SequencedEvent",err); - } - } -} diff --git a/test/jdk/java/awt/im/8041990/bug8041990.java b/test/jdk/java/awt/im/8041990/bug8041990.java deleted file mode 100644 index c5ddbac42ba..00000000000 --- a/test/jdk/java/awt/im/8041990/bug8041990.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2014, 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. - * - * 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 - * @key headful - * @bug 8041990 - * @summary Language specific keys does not work in non-default AppContexts of top-level windows - * @author Petr Pchelko - * @modules java.desktop/sun.awt - */ - -import sun.awt.SunToolkit; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.InputMethodEvent; -import java.awt.font.TextHitInfo; -import java.text.AttributedString; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicReference; - -public class bug8041990 { - private static JFrame frame; - private static JComponent component; - - public static void main(String[] args) throws Exception { - ThreadGroup stubTG = new ThreadGroup(getRootThreadGroup(), "Stub Thread Group"); - ThreadGroup swingTG = new ThreadGroup(getRootThreadGroup(), "SwingTG"); - try { - Thread stubThread = new Thread(stubTG, SunToolkit::createNewAppContext); - stubThread.start(); - stubThread.join(); - - CountDownLatch startSwingLatch = new CountDownLatch(1); - new Thread(swingTG, () -> { - SunToolkit.createNewAppContext(); - SwingUtilities.invokeLater(() -> { - frame = new JFrame(); - component = new JLabel("Test Text"); - frame.add(component); - frame.setBounds(100, 100, 100, 100); - frame.setVisible(true); - startSwingLatch.countDown(); - }); - }).start(); - startSwingLatch.await(); - - AtomicReference caughtException = new AtomicReference<>(); - Thread checkThread = new Thread(getRootThreadGroup(), () -> { - try { - // If the bug is present this will throw exception - new InputMethodEvent(component, - InputMethodEvent.CARET_POSITION_CHANGED, - TextHitInfo.leading(0), - TextHitInfo.trailing(0)); - } catch (Exception e) { - caughtException.set(e); - } - }); - checkThread.start(); - checkThread.join(); - - if (caughtException.get() != null) { - throw new RuntimeException("Failed. Caught exception!", caughtException.get()); - } - } finally { - new Thread(swingTG, () -> SwingUtilities.invokeLater(() -> { - if (frame != null) { - frame.dispose(); - } - })).start(); - } - } - - private static ThreadGroup getRootThreadGroup() { - ThreadGroup currentTG = Thread.currentThread().getThreadGroup(); - ThreadGroup parentTG = currentTG.getParent(); - while (parentTG != null) { - currentTG = parentTG; - parentTG = currentTG.getParent(); - } - return currentTG; - } -} diff --git a/test/jdk/java/awt/print/PrinterJob/PageRangesAuto.java b/test/jdk/java/awt/print/PrinterJob/PageRangesAuto.java new file mode 100644 index 00000000000..8d50ef9c2c2 --- /dev/null +++ b/test/jdk/java/awt/print/PrinterJob/PageRangesAuto.java @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2007, 2026, 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.awt.Font; +import java.awt.Graphics; +import java.awt.print.PageFormat; +import java.awt.print.Pageable; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.List; +import java.util.stream.IntStream; + +import javax.print.attribute.HashPrintRequestAttributeSet; +import javax.print.attribute.PrintRequestAttributeSet; +import javax.print.attribute.standard.Destination; +import javax.print.attribute.standard.PageRanges; + +/* + * @test + * @bug 6575331 8297191 + * @key printer + * @summary Automatically verifies all the pages in a range are printed + * @run main PageRangesAuto + */ +public class PageRangesAuto implements Pageable, Printable { + + private static final Font font = new Font(Font.SERIF, Font.PLAIN, 50); + + private static final int MAX_PAGE = 10; + + private static final int[][] ranges = { + {1, 1}, + {1, MAX_PAGE}, + {2, 3}, + {3, 6}, + {4, 7}, + {7, 7}, + {9, MAX_PAGE}, + {MAX_PAGE, MAX_PAGE}, + }; + + private enum Type { + PRINTABLE, + PAGEABLE + } + + private final BitSet printedPages = new BitSet(); + + /** + * Configures a printer job and prints it. + * @param type the type of the interface tested + * ({@code Printable} or {@code Pageable}) + * @param pageRange the range of pages to print; + * if {@code null}, print all pages + * @return a bit set of printed page numbers + */ + private static BitSet printJob(final Type type, + final PageRanges pageRange) + throws PrinterException { + final PageRangesAuto test = new PageRangesAuto(); + + final PrinterJob job = PrinterJob.getPrinterJob(); + final String baseName = type.name().toLowerCase(); + + switch (type) { + case PRINTABLE -> job.setPrintable(test); + case PAGEABLE -> job.setPageable(test); + } + + String fileName = pageRange == null + ? baseName + "-all.pdf" + : String.format("%s-%d-%d.pdf", + baseName, + pageRange.getMembers()[0][0], + pageRange.getMembers()[0][1]); + + PrintRequestAttributeSet set = new HashPrintRequestAttributeSet(); + set.add(new Destination(new File(fileName) + .toURI())); + if (pageRange != null) { + set.add(pageRange); + } + + job.print(set); + + return test.printedPages; + } + + public static void main(String[] args) throws Exception { + final List errors = new ArrayList<>(); + + for (Type type : Type.values()) { + BitSet pages; // Printed pages + + // Print all pages + System.out.println(type + " - all pages"); + pages = printJob(type, null); + if (!IntStream.range(0, MAX_PAGE) + .allMatch(pages::get)) { + errors.add(new Error("Not all pages printed in " + type + ": " + + pages)); + } + + // Print page range + for (int[] range : ranges) { + System.out.println(type + " - " + Arrays.toString(range)); + pages = printJob(type, new PageRanges(range[0], range[1])); + if (!IntStream.range(range[0] - 1, range[1]) + .allMatch(pages::get)) { + errors.add(new Error("Not all pages printed in " + type + + " within the range " + + Arrays.toString(range) + + ": " + pages)); + } + } + } + + if (!errors.isEmpty()) { + errors.forEach(System.err::println); + throw new RuntimeException("Errors detected: " + errors.size() + + ". - " + errors.getFirst()); + } + } + + @Override + public int print(Graphics g, PageFormat format, int pageIndex) + throws PrinterException { + printedPages.set(pageIndex); + + final int pageNo = pageIndex + 1; + System.out.println(" test.printPage " + pageNo); + if (pageIndex >= MAX_PAGE) { + return NO_SUCH_PAGE; + } + + g.setFont(font); + g.drawString("Page: " + pageNo, + 100, 150); + + return PAGE_EXISTS; + } + + @Override + public int getNumberOfPages() { + System.out.println(" test.getNumberOfPages = " + MAX_PAGE); + return MAX_PAGE; + } + + @Override + public PageFormat getPageFormat(int pageIndex) + throws IndexOutOfBoundsException { + checkPageIndex(pageIndex); + return new PageFormat(); + } + + @Override + public Printable getPrintable(int pageIndex) + throws IndexOutOfBoundsException { + checkPageIndex(pageIndex); + System.out.println(" test.getPrintable(" + (pageIndex + 1) + ")"); + return this; + } + + private static void checkPageIndex(int pageIndex) + throws IndexOutOfBoundsException { + if (pageIndex < 0) { + throw new IndexOutOfBoundsException("pageIndex < 0"); + } + + if (pageIndex >= MAX_PAGE) { + throw new IndexOutOfBoundsException("pageIndex >= " + MAX_PAGE); + } + } +} diff --git a/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java b/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java index 2c69c5691b8..8ef5483bd82 100644 --- a/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java +++ b/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -73,7 +73,7 @@ public class TestCaptureCallState extends NativeTestHelper { } private record SaveValuesCase(String nativeTarget, FunctionDescriptor nativeDesc, String threadLocalName, - Consumer resultCheck, boolean critical) {} + Consumer resultCheck, boolean expectTestValue, boolean critical) {} @Test(dataProvider = "cases") public void testSavedThreadLocal(SaveValuesCase testCase) throws Throwable { @@ -90,14 +90,21 @@ public class TestCaptureCallState extends NativeTestHelper { try (Arena arena = Arena.ofConfined()) { MemorySegment saveSeg = arena.allocate(capturedStateLayout); + // The existing value and the value that it should be overwritten with + int prevValue = 24; int testValue = 42; + errnoHandle.set(saveSeg, 0L, prevValue); boolean needsAllocator = testCase.nativeDesc().returnLayout().map(StructLayout.class::isInstance).orElse(false); Object result = needsAllocator ? handle.invoke(arena, saveSeg, testValue) : handle.invoke(saveSeg, testValue); testCase.resultCheck().accept(result); int savedErrno = (int) errnoHandle.get(saveSeg, 0L); - assertEquals(savedErrno, testValue); + if (testCase.expectTestValue()) { + assertEquals(savedErrno, testValue); + } else { + assertEquals(savedErrno, prevValue); + } } } @@ -127,37 +134,45 @@ public class TestCaptureCallState extends NativeTestHelper { for (boolean critical : new boolean[]{ true, false }) { cases.add(new SaveValuesCase("set_errno_V", FunctionDescriptor.ofVoid(JAVA_INT), - "errno", o -> {}, critical)); + "errno", o -> {}, true, critical)); + cases.add(new SaveValuesCase("noset_errno_V", FunctionDescriptor.ofVoid(JAVA_INT), + "errno", o -> {}, false, critical)); cases.add(new SaveValuesCase("set_errno_I", FunctionDescriptor.of(JAVA_INT, JAVA_INT), - "errno", o -> assertEquals((int) o, 42), critical)); + "errno", o -> assertEquals((int) o, 42), true, critical)); cases.add(new SaveValuesCase("set_errno_D", FunctionDescriptor.of(JAVA_DOUBLE, JAVA_INT), - "errno", o -> assertEquals((double) o, 42.0), critical)); + "errno", o -> assertEquals((double) o, 42.0), true, critical)); - cases.add(structCase("SL", Map.of(JAVA_LONG.withName("x"), 42L), critical)); + cases.add(structCase("SL", Map.of(JAVA_LONG.withName("x"), 42L), true, critical)); cases.add(structCase("SLL", Map.of(JAVA_LONG.withName("x"), 42L, - JAVA_LONG.withName("y"), 42L), critical)); + JAVA_LONG.withName("y"), 42L), true, critical)); cases.add(structCase("SLLL", Map.of(JAVA_LONG.withName("x"), 42L, JAVA_LONG.withName("y"), 42L, - JAVA_LONG.withName("z"), 42L), critical)); - cases.add(structCase("SD", Map.of(JAVA_DOUBLE.withName("x"), 42D), critical)); + JAVA_LONG.withName("z"), 42L), true, critical)); + cases.add(structCase("SLLL", Map.of(JAVA_LONG.withName("x"), 42L, + JAVA_LONG.withName("y"), 42L, + JAVA_LONG.withName("z"), 42L), false, critical)); + cases.add(structCase("SD", Map.of(JAVA_DOUBLE.withName("x"), 42D), true, critical)); cases.add(structCase("SDD", Map.of(JAVA_DOUBLE.withName("x"), 42D, - JAVA_DOUBLE.withName("y"), 42D), critical)); + JAVA_DOUBLE.withName("y"), 42D), true, critical)); cases.add(structCase("SDDD", Map.of(JAVA_DOUBLE.withName("x"), 42D, JAVA_DOUBLE.withName("y"), 42D, - JAVA_DOUBLE.withName("z"), 42D), critical)); + JAVA_DOUBLE.withName("z"), 42D), true, critical)); if (IS_WINDOWS) { cases.add(new SaveValuesCase("SetLastError", FunctionDescriptor.ofVoid(JAVA_INT), - "GetLastError", o -> {}, critical)); + "GetLastError", o -> {}, true, critical)); cases.add(new SaveValuesCase("WSASetLastError", FunctionDescriptor.ofVoid(JAVA_INT), - "WSAGetLastError", o -> {}, critical)); + "WSAGetLastError", o -> {}, true, critical)); } } return cases.stream().map(tc -> new Object[] {tc}).toArray(Object[][]::new); } - static SaveValuesCase structCase(String name, Map fields, boolean critical) { + static SaveValuesCase structCase(String name, + Map fields, + boolean expectTestValue, + boolean critical) { StructLayout layout = MemoryLayout.structLayout(fields.keySet().toArray(MemoryLayout[]::new)); Consumer check = o -> {}; @@ -167,9 +182,9 @@ public class TestCaptureCallState extends NativeTestHelper { Object value = field.getValue(); check = check.andThen(o -> assertEquals(fieldHandle.get(o, 0L), value)); } - - return new SaveValuesCase("set_errno_" + name, FunctionDescriptor.of(layout, JAVA_INT), - "errno", check, critical); + String prefix = expectTestValue ? "set_errno_" : "noset_errno_"; + return new SaveValuesCase(prefix + name, FunctionDescriptor.of(layout, JAVA_INT), + "errno", check, expectTestValue, critical); } @DataProvider diff --git a/test/jdk/java/foreign/capturecallstate/libCaptureCallState.c b/test/jdk/java/foreign/capturecallstate/libCaptureCallState.c index d76c9d7beda..fe0d144cb5b 100644 --- a/test/jdk/java/foreign/capturecallstate/libCaptureCallState.c +++ b/test/jdk/java/foreign/capturecallstate/libCaptureCallState.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -29,6 +29,10 @@ EXPORT void set_errno_V(int value) { errno = value; } +EXPORT int noset_errno_V(int value) { + return 42; +} + EXPORT int set_errno_I(int value) { errno = value; return 42; @@ -78,6 +82,14 @@ EXPORT struct SLLL set_errno_SLLL(int value) { return s; } +EXPORT struct SLLL noset_errno_SLLL(int value) { + struct SLLL s; + s.x = 42; + s.y = 42; + s.z = 42; + return s; +} + struct SD { double x; }; diff --git a/test/jdk/java/io/BufferedInputStream/TransferTo.java b/test/jdk/java/io/BufferedInputStream/TransferTo.java index a0d7b0ee9bd..fe42c0a8b2e 100644 --- a/test/jdk/java/io/BufferedInputStream/TransferTo.java +++ b/test/jdk/java/io/BufferedInputStream/TransferTo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -46,22 +46,22 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Supplier; -import org.testng.annotations.Test; - import jdk.test.lib.RandomFactory; import static java.lang.String.format; import static java.nio.file.StandardOpenOption.*; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @library /test/lib * @build jdk.test.lib.RandomFactory - * @run testng/othervm/timeout=180 -Xmx1280m TransferTo + * @run junit/othervm/timeout=180 -Xmx1280m TransferTo * @bug 8279283 8294541 * @summary Tests whether java.io.BufferedInputStream.transferTo conforms to the * InputStream.transferTo specification @@ -183,7 +183,7 @@ public class TransferTo { int count = inBytes.length - posIn; int expected = count - bufferBytes; - assertEquals(reported, expected, + assertEquals(expected, reported, format("transferred %d bytes but should report %d", reported, expected)); byte[] outBytes = recorder.get().get(); @@ -198,7 +198,7 @@ public class TransferTo { reported = in.transferTo(out); expected = count - bufferBytes; - assertEquals(reported, expected, + assertEquals(expected, reported, format("replayed %d bytes but should report %d", reported, expected)); outBytes = recorder.get().get(); diff --git a/test/jdk/java/io/BufferedReader/Lines.java b/test/jdk/java/io/BufferedReader/Lines.java index 2e35f75887d..3b4edb80e70 100644 --- a/test/jdk/java/io/BufferedReader/Lines.java +++ b/test/jdk/java/io/BufferedReader/Lines.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, 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 @@ -24,7 +24,7 @@ /* * @test * @bug 8003258 8029434 - * @run testng Lines + * @run junit Lines */ import java.io.BufferedReader; @@ -41,10 +41,15 @@ import java.util.NoSuchElementException; import java.util.Spliterator; import java.util.stream.Stream; import java.util.concurrent.atomic.AtomicInteger; -import org.testng.annotations.Test; -import static org.testng.Assert.*; -@Test(groups = "unit") +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + public class Lines { private static final Map cases = new HashMap<>(); @@ -155,75 +160,81 @@ public class Lines { private static void verify(Map.Entry e) { final String data = e.getKey(); final int total_lines = e.getValue(); - try (BufferedReader br = new BufferedReader( - new StringReader(data))) { - assertEquals(br.lines() - .mapToInt(l -> 1).reduce(0, (x, y) -> x + y), - total_lines, - data + " should produce " + total_lines + " lines."); - } catch (IOException ioe) { - fail("Should not have any exception."); - } + assertDoesNotThrow + (() -> { + try (BufferedReader br = + new BufferedReader(new StringReader(data))) { + assertEquals(total_lines, + br.lines().mapToInt(l -> 1).reduce(0, (x, y) -> x + y), + data + " should produce " + total_lines + " lines."); + } + }); } + @Test public void testLinesBasic() { // Basic test cases cases.entrySet().stream().forEach(Lines::verify); // Similar test, also verify MockLineReader is correct - for (int i = 0; i < 10; i++) { - try (BufferedReader br = new BufferedReader(new MockLineReader(i))) { - assertEquals(br.lines() - .peek(l -> assertTrue(l.matches("^Line \\d+$"))) - .mapToInt(l -> 1).reduce(0, (x, y) -> x + y), - i, - "MockLineReader(" + i + ") should produce " + i + " lines."); - } catch (IOException ioe) { - fail("Unexpected IOException."); - } - } + assertDoesNotThrow + (() -> { + for (int i = 0; i < 10; i++) { + try (BufferedReader br = + new BufferedReader(new MockLineReader(i))) { + assertEquals(i, + br.lines() + .peek(l -> assertTrue(l.matches("^Line \\d+$"))) + .mapToInt(l -> 1).reduce(0, (x, y) -> x + y), + "MockLineReader(" + i + ") should produce " + i + " lines."); + } + } + }); } + @Test public void testUncheckedIOException() throws IOException { MockLineReader r = new MockLineReader(10, 3); ArrayList ar = new ArrayList<>(); - try (BufferedReader br = new BufferedReader(r)) { - br.lines().limit(3L).forEach(ar::add); - assertEquals(ar.size(), 3, "Should be able to read 3 lines."); - } catch (UncheckedIOException uioe) { - fail("Unexpected UncheckedIOException"); - } + assertDoesNotThrow + (() -> { + try (BufferedReader br = new BufferedReader(r)) { + br.lines().limit(3L).forEach(ar::add); + assertEquals(3, ar.size(), "Should be able to read 3 lines."); + } + }); r.reset(); - try (BufferedReader br = new BufferedReader(r)) { - br.lines().forEach(ar::add); - fail("Should had thrown UncheckedIOException."); - } catch (UncheckedIOException uioe) { - assertEquals(r.getLineNumber(), 4, "should fail to read 4th line"); - assertEquals(ar.size(), 6, "3 + 3 lines read"); - } + assertThrows(UncheckedIOException.class, + () -> { + try (BufferedReader br = new BufferedReader(r)) { + br.lines().forEach(ar::add); + } + }); + assertEquals(4, r.getLineNumber(), "should fail to read 4th line"); + assertEquals(6, ar.size(), "3 + 3 lines read"); for (int i = 0; i < ar.size(); i++) { - assertEquals(ar.get(i), "Line " + (i % 3 + 1)); + assertEquals("Line " + (i % 3 + 1), ar.get(i)); } } + @Test public void testIterator() throws IOException { MockLineReader r = new MockLineReader(6); BufferedReader br = new BufferedReader(r); String line = br.readLine(); - assertEquals(r.getLineNumber(), 1, "Read one line"); + assertEquals(1, r.getLineNumber(), "Read one line"); Stream s = br.lines(); Iterator it = s.iterator(); // Ensure iterate with only next works for (int i = 0; i < 5; i++) { String str = it.next(); - assertEquals(str, "Line " + (i + 2), "Addtional five lines"); + assertEquals("Line " + (i + 2), str, "Addtional five lines"); } // NoSuchElementException - try { - it.next(); - fail("Should have run out of lines."); - } catch (NoSuchElementException nsse) {} + assertThrows(NoSuchElementException.class, () -> it.next(), + "Should have run out of lines."); } + @Test public void testPartialReadAndLineNo() throws IOException { MockLineReader r = new MockLineReader(5); LineNumberReader lr = new LineNumberReader(r); @@ -231,23 +242,24 @@ public class Lines { lr.read(buf, 0, 5); assertEquals(0, lr.getLineNumber(), "LineNumberReader start with line 0"); assertEquals(1, r.getLineNumber(), "MockLineReader start with line 1"); - assertEquals(new String(buf), "Line "); + assertEquals("Line ", new String(buf)); String l1 = lr.readLine(); - assertEquals(l1, "1", "Remaining of the first line"); + assertEquals("1", l1, "Remaining of the first line"); assertEquals(1, lr.getLineNumber(), "Line 1 is read"); assertEquals(1, r.getLineNumber(), "MockLineReader not yet go next line"); lr.read(buf, 0, 4); assertEquals(1, lr.getLineNumber(), "In the middle of line 2"); - assertEquals(new String(buf, 0, 4), "Line"); + assertEquals("Line", new String(buf, 0, 4)); ArrayList ar = lr.lines() .peek(l -> assertEquals(lr.getLineNumber(), r.getLineNumber())) .collect(ArrayList::new, ArrayList::add, ArrayList::addAll); - assertEquals(ar.get(0), " 2", "Remaining in the second line"); + assertEquals(" 2", ar.get(0), "Remaining in the second line"); for (int i = 1; i < ar.size(); i++) { - assertEquals(ar.get(i), "Line " + (i + 2), "Rest are full lines"); + assertEquals("Line " + (i + 2), ar.get(i), "Rest are full lines"); } } + @Test public void testInterlacedRead() throws IOException { MockLineReader r = new MockLineReader(10); BufferedReader br = new BufferedReader(r); @@ -256,26 +268,24 @@ public class Lines { Iterator it = s.iterator(); br.read(buf); - assertEquals(new String(buf), "Line "); - assertEquals(it.next(), "1"); - try { - s.iterator().next(); - fail("Should failed on second attempt to get iterator from s"); - } catch (IllegalStateException ise) {} + assertEquals("Line ", new String(buf)); + assertEquals("1", it.next()); + assertThrows(IllegalStateException.class, () -> s.iterator().next(), + "Should fail on second call to Iterator next method"); br.read(buf, 0, 2); - assertEquals(new String(buf, 0, 2), "Li"); + assertEquals("Li", new String(buf, 0, 2)); // Get stream again should continue from where left // Only read remaining of the line br.lines().limit(1L).forEach(line -> assertEquals(line, "ne 2")); br.read(buf, 0, 2); - assertEquals(new String(buf, 0, 2), "Li"); + assertEquals("Li", new String(buf, 0, 2)); br.read(buf, 0, 2); - assertEquals(new String(buf, 0, 2), "ne"); - assertEquals(it.next(), " 3"); + assertEquals("ne", new String(buf, 0, 2)); + assertEquals(" 3", it.next()); // Line 4 br.readLine(); // interator pick - assertEquals(it.next(), "Line 5"); + assertEquals("Line 5", it.next()); // Another stream instantiated by lines() AtomicInteger line_no = new AtomicInteger(6); br.lines().forEach(l -> assertEquals(l, "Line " + line_no.getAndIncrement())); @@ -283,14 +293,16 @@ public class Lines { assertFalse(it.hasNext()); } + @Test public void testCharacteristics() { - try (BufferedReader br = new BufferedReader( - new StringReader(""))) { - Spliterator instance = br.lines().spliterator(); - assertTrue(instance.hasCharacteristics(Spliterator.NONNULL)); - assertTrue(instance.hasCharacteristics(Spliterator.ORDERED)); - } catch (IOException ioe) { - fail("Should not have any exception."); - } + assertDoesNotThrow + (() -> { + try (BufferedReader br = + new BufferedReader(new StringReader(""))) { + Spliterator instance = br.lines().spliterator(); + assertTrue(instance.hasCharacteristics(Spliterator.NONNULL)); + assertTrue(instance.hasCharacteristics(Spliterator.ORDERED)); + } + }); } } diff --git a/test/jdk/java/io/ByteArrayOutputStream/EncodingTest.java b/test/jdk/java/io/ByteArrayOutputStream/EncodingTest.java index 1a9d8d4c20b..40bef97294a 100644 --- a/test/jdk/java/io/ByteArrayOutputStream/EncodingTest.java +++ b/test/jdk/java/io/ByteArrayOutputStream/EncodingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -25,29 +25,31 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @test * @bug 8183743 * @summary Test to verify the new overload method with Charset functions the same * as the existing method that takes a charset name. - * @run testng EncodingTest + * @run junit EncodingTest */ public class EncodingTest { /* - * DataProvider for the toString method test. Provides the following fields: + * MethodSource for the toString method test. Provides the following fields: * byte array, charset name string, charset object */ - @DataProvider(name = "parameters") - public Object[][] getParameters() throws IOException { + public static Stream parameters() throws IOException { byte[] data = getData(); - return new Object[][]{ - {data, StandardCharsets.UTF_8.name(), StandardCharsets.UTF_8}, - {data, StandardCharsets.ISO_8859_1.name(), StandardCharsets.ISO_8859_1}, - }; + return Stream.of + (Arguments.of(data, StandardCharsets.UTF_8.name(), StandardCharsets.UTF_8), + Arguments.of(data, StandardCharsets.ISO_8859_1.name(), StandardCharsets.ISO_8859_1)); } /** @@ -58,20 +60,21 @@ public class EncodingTest { * @param charset the charset * @throws Exception if the test fails */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("parameters") public void test(byte[] data, String csn, Charset charset) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(data); String str1 = baos.toString(csn); String str2 = baos.toString(charset); - Assert.assertEquals(str1, str2); + assertEquals(str2, str1); } /* * Returns an array containing a character that's invalid for UTF-8 * but valid for ISO-8859-1 */ - byte[] getData() throws IOException { + static byte[] getData() throws IOException { String str1 = "A string that contains "; String str2 = " , an invalid character for UTF-8."; diff --git a/test/jdk/java/io/ByteArrayOutputStream/Write.java b/test/jdk/java/io/ByteArrayOutputStream/Write.java index 72c399916e0..99d8a206635 100644 --- a/test/jdk/java/io/ByteArrayOutputStream/Write.java +++ b/test/jdk/java/io/ByteArrayOutputStream/Write.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -26,7 +26,7 @@ * @bug 4017158 8180410 * @library /test/lib * @build jdk.test.lib.RandomFactory - * @run testng Write + * @run junit Write * @summary Check for correct implementation of ByteArrayInputStream.write * @key randomness */ @@ -35,49 +35,40 @@ import java.io.ByteArrayOutputStream; import java.util.Arrays; import java.util.Random; import jdk.test.lib.RandomFactory; -import org.testng.annotations.Test; -import static org.testng.Assert.*; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; public class Write { private static void doBoundsTest(byte[] b, int off, int len, ByteArrayOutputStream baos) throws Exception { if (b != null) { - System.out.println("ByteArrayOutStream.write: b.length = " + + System.err.println("ByteArrayOutStream.write: b.length = " + b.length + " off = " + off + " len = " + len); } else{ - System.out.println("ByteArrayOutStream.write: b is null off = " + + System.err.println("ByteArrayOutStream.write: b is null off = " + off + " len = " + len); } - try { - baos.write(b, off, len); - } catch (IndexOutOfBoundsException e) { - System.out.println("IndexOutOfBoundsException is thrown: OKAY"); - } catch (NullPointerException e) { - System.out.println("NullPointerException is thrown: OKAY"); - } catch (Throwable e){ - throw new RuntimeException("Unexpected Exception is thrown", e); - } + Class expectedException = (b == null) + ? NullPointerException.class : IndexOutOfBoundsException.class; + assertThrows(expectedException, () -> baos.write(b, off, len)); if (b != null) { - System.out.println("ByteArrayOutStream.writeBytes: b.length = " + + System.err.println("ByteArrayOutStream.writeBytes: b.length = " + b.length); } else{ - System.out.println("ByteArrayOutStream.writeBytes: b is null"); - } - - try { - baos.writeBytes(b); - } catch (NullPointerException e) { - System.out.println("NullPointerException is thrown: OKAY"); - } catch (Throwable e){ - throw new RuntimeException("Unexpected Exception is thrown", e); + System.err.println("ByteArrayOutStream.writeBytes: b is null"); + assertThrows(NullPointerException.class, () -> baos.writeBytes(b)); } } @Test - public static void boundsTest() throws Exception { + public void boundsTest() throws Exception { byte array1[] = {1 , 2 , 3 , 4 , 5}; // Simple array //Create new ByteArrayOutputStream object @@ -91,7 +82,7 @@ public class Write { } @Test - public static void writeTest() throws Exception { + public void writeTest() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); Random rnd = RandomFactory.getRandom(); final int size = 17 + rnd.nextInt(128); @@ -104,39 +95,36 @@ public class Write { int off2 = rnd.nextInt(size / 2) + 1; int len2 = Math.min(rnd.nextInt(size / 2) + 1, size - off2); - System.out.format("size: %d, off1: %d, len1: %d, off2: %d, len2: %d%n", + System.err.format("size: %d, off1: %d, len1: %d, off2: %d, len2: %d%n", size, off1, len1, off2, len2); baos.write(b, off1, len1); byte[] b1 = baos.toByteArray(); - assertEquals(b1.length, len1, "Array length test 1 failed."); - assertEquals(b1, Arrays.copyOfRange(b, off1, off1 + len1), + assertEquals(len1, b1.length, "Array length test 1 failed."); + assertArrayEquals(Arrays.copyOfRange(b, off1, off1 + len1), b1, "Array equality test 1 failed."); baos.write(b, off2, len2); byte[] b2 = baos.toByteArray(); - assertEquals(b2.length, len1 + len2, "Array length test 2 failed."); - assertEquals(Arrays.copyOfRange(b2, 0, len1), - Arrays.copyOfRange(b, off1, off1 + len1), + assertEquals(len1 + len2, b2.length, "Array length test 2 failed."); + assertArrayEquals(Arrays.copyOfRange(b, off1, off1 + len1), + Arrays.copyOfRange(b2, 0, len1), "Array equality test 2A failed."); - assertEquals(Arrays.copyOfRange(b2, len1, len1 + len2), - Arrays.copyOfRange(b, off2, off2 + len2), + assertArrayEquals(Arrays.copyOfRange(b, off2, off2 + len2), + Arrays.copyOfRange(b2, len1, len1 + len2), "Array equality test 2B failed."); baos.writeBytes(b); byte[] b3 = baos.toByteArray(); int len3 = len1 + len2 + b.length; - if (b3.length != len1 + len2 + b.length) { - throw new RuntimeException("Array length test 3 failed."); - } - assertEquals(b3.length, len3, "Array length test 3 failed."); - assertEquals(Arrays.copyOfRange(b3, 0, len1), - Arrays.copyOfRange(b, off1, off1 + len1), + assertEquals(len3, b3.length, "Array length test 3 failed."); + assertArrayEquals(Arrays.copyOfRange(b, off1, off1 + len1), + Arrays.copyOfRange(b3, 0, len1), "Array equality test 3A failed."); - assertEquals(Arrays.copyOfRange(b3, len1, len1 + len2), - Arrays.copyOfRange(b, off2, off2 + len2), + assertArrayEquals(Arrays.copyOfRange(b, off2, off2 + len2), + Arrays.copyOfRange(b3, len1, len1 + len2), "Array equality test 3B failed."); - assertEquals(Arrays.copyOfRange(b3, len1 + len2, len3), b, + assertArrayEquals(b, Arrays.copyOfRange(b3, len1 + len2, len3), "Array equality test 3C failed."); } } diff --git a/test/jdk/java/io/CharArrayReader/ReadCharBuffer.java b/test/jdk/java/io/CharArrayReader/ReadCharBuffer.java index 545ba8ba029..87571a399ac 100644 --- a/test/jdk/java/io/CharArrayReader/ReadCharBuffer.java +++ b/test/jdk/java/io/CharArrayReader/ReadCharBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -25,13 +25,9 @@ * @test * @bug 4926314 * @summary Test for CharArrayReader#read(CharBuffer). - * @run testng ReadCharBuffer + * @run junit ReadCharBuffer */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - - import java.io.CharArrayReader; import java.io.IOException; import java.io.Reader; @@ -39,43 +35,46 @@ import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.util.Arrays; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class ReadCharBuffer { private static final int BUFFER_SIZE = 7; - @DataProvider(name = "buffers") - public Object[][] createBuffers() { + public static CharBuffer[] buffers() { // test both on-heap and off-heap buffers as they may use different code paths - return new Object[][]{ - new Object[]{CharBuffer.allocate(BUFFER_SIZE)}, - new Object[]{ByteBuffer.allocateDirect(BUFFER_SIZE * 2).asCharBuffer()} + return new CharBuffer[] { + CharBuffer.allocate(BUFFER_SIZE), + ByteBuffer.allocateDirect(BUFFER_SIZE * 2).asCharBuffer() }; } - @Test(dataProvider = "buffers") + @ParameterizedTest + @MethodSource("buffers") public void read(CharBuffer buffer) throws IOException { fillBuffer(buffer); try (Reader reader = new CharArrayReader("ABCD".toCharArray())) { buffer.limit(3); buffer.position(1); - assertEquals(reader.read(buffer), 2); - assertEquals(buffer.position(), 3); - assertEquals(buffer.limit(), 3); + assertEquals(2, reader.read(buffer)); + assertEquals(3, buffer.position()); + assertEquals(3, buffer.limit()); buffer.limit(7); buffer.position(4); - assertEquals(reader.read(buffer), 2); - assertEquals(buffer.position(), 6); - assertEquals(buffer.limit(), 7); + assertEquals(2, reader.read(buffer)); + assertEquals(6, buffer.position()); + assertEquals(7, buffer.limit()); - assertEquals(reader.read(buffer), -1); + assertEquals(-1, reader.read(buffer)); } buffer.clear(); - assertEquals(buffer.toString(), "xABxCDx"); + assertEquals("xABxCDx", buffer.toString()); } private void fillBuffer(CharBuffer buffer) { diff --git a/test/jdk/java/io/DataOutputStream/WriteUTF.java b/test/jdk/java/io/DataOutputStream/WriteUTF.java index d5557ec85ed..ed8b7397971 100644 --- a/test/jdk/java/io/DataOutputStream/WriteUTF.java +++ b/test/jdk/java/io/DataOutputStream/WriteUTF.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -25,7 +25,7 @@ * @bug 4260284 8219196 8223254 * @summary Test if DataOutputStream will overcount written field. * @requires (sun.arch.data.model == "64" & os.maxMemory >= 4g) - * @run testng/othervm -Xmx4g WriteUTF + * @run junit/othervm -Xmx4g WriteUTF */ import java.io.ByteArrayOutputStream; @@ -33,11 +33,13 @@ import java.io.DataOutputStream; import java.io.IOException; import java.io.UTFDataFormatException; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; public class WriteUTF { @Test - public static void overcountWrittenField() throws IOException { + public void overcountWrittenField() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); dos.writeUTF("Hello, World!"); // 15 @@ -54,16 +56,16 @@ public class WriteUTF { dos.writeUTF(s); } - @Test(expectedExceptions = UTFDataFormatException.class) + @Test public void utfDataFormatException() throws IOException { - writeUTF(1 << 16); + assertThrows(UTFDataFormatException.class, () -> writeUTF(1 << 16)); } // Without 8219196 fix, throws ArrayIndexOutOfBoundsException instead of // expected UTFDataFormatException. Requires 4GB of heap (-Xmx4g) to run // without throwing an OutOfMemoryError. - @Test(expectedExceptions = UTFDataFormatException.class) + @Test public void arrayIndexOutOfBoundsException() throws IOException { - writeUTF(Integer.MAX_VALUE / 3 + 1); + assertThrows(UTFDataFormatException.class, () -> writeUTF(Integer.MAX_VALUE / 3 + 1)); } } diff --git a/test/jdk/java/io/File/EmptyPath.java b/test/jdk/java/io/File/EmptyPath.java index 374a69c7959..67331380531 100644 --- a/test/jdk/java/io/File/EmptyPath.java +++ b/test/jdk/java/io/File/EmptyPath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -285,17 +285,6 @@ public class EmptyPath { assertEquals(nioSet, ioSet); } - @Test - public void listRoots() { - Set expected = Arrays.stream(f.getAbsoluteFile().listRoots()) - .map(File::toString) - .collect(Collectors.toSet()); - Set actual = Arrays.stream(f.listRoots()) - .map(File::toString) - .collect(Collectors.toSet()); - assertEquals(expected, actual); - } - @Test public void mkdir() { assertFalse(f.mkdir()); diff --git a/test/jdk/java/io/File/LastModifiedTest.java b/test/jdk/java/io/File/LastModifiedTest.java index 52c7d390401..4eaf5fa34c2 100644 --- a/test/jdk/java/io/File/LastModifiedTest.java +++ b/test/jdk/java/io/File/LastModifiedTest.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -21,20 +22,19 @@ * questions. */ -import org.testng.annotations.Test; - import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.time.Instant; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotEquals; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @test * @library .. - * @run testng LastModifiedTest + * @run junit LastModifiedTest * @summary Test to validate that java.nio.Files returns the same value * as java.io.File */ @@ -51,7 +51,7 @@ public class LastModifiedTest { long ioTimestamp = tempFile.lastModified(); long nioTimestamp = Files.getLastModifiedTime(tempFile.toPath()).toMillis(); - assertEquals(ioTimestamp, nioTimestamp); + assertEquals(nioTimestamp, ioTimestamp); } finally { tempFile.delete(); } diff --git a/test/jdk/java/io/FileReader/ConstructorTest.java b/test/jdk/java/io/FileReader/ConstructorTest.java index b082a710079..71cd525917a 100644 --- a/test/jdk/java/io/FileReader/ConstructorTest.java +++ b/test/jdk/java/io/FileReader/ConstructorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -30,15 +30,19 @@ import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @test * @bug 8183554 * @summary Test to verify the new Constructors that take a Charset. - * @run testng ConstructorTest + * @run junit ConstructorTest */ public class ConstructorTest { static String USER_DIR = System.getProperty("user.dir", "."); @@ -51,17 +55,15 @@ public class ConstructorTest { static final String TEST_STRING = "abc \u0100 \u0101 \u0555 \u07FD \u07FF"; static final int BUFFER_SIZE = 8192; - @DataProvider(name = "parameters") - public Object[][] getParameters() throws IOException { + public static Stream parameters() throws IOException { File file1 = new File(USER_DIR, "FileReaderTest1.txt"); File file2 = new File(USER_DIR, "FileReaderTest2.txt"); - return new Object[][]{ - {ConstructorType.STRING, file1, file2, StandardCharsets.UTF_8}, - {ConstructorType.FILE, file1, file2, StandardCharsets.UTF_8}, - {ConstructorType.STRING, file1, file2, StandardCharsets.ISO_8859_1}, - {ConstructorType.FILE, file1, file2, StandardCharsets.ISO_8859_1}, - }; + return Stream.of + (Arguments.of(ConstructorType.STRING, file1, file2, StandardCharsets.UTF_8), + Arguments.of(ConstructorType.FILE, file1, file2, StandardCharsets.UTF_8), + Arguments.of(ConstructorType.STRING, file1, file2, StandardCharsets.ISO_8859_1), + Arguments.of(ConstructorType.FILE, file1, file2, StandardCharsets.ISO_8859_1)); } /** @@ -75,7 +77,8 @@ public class ConstructorTest { * @param charset the charset * @throws IOException */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("parameters") void test(ConstructorType type, File file1, File file2, Charset charset) throws Exception { prepareFile(file1, TEST_STRING, charset); @@ -86,7 +89,7 @@ public class ConstructorTest { InputStreamReader isr = new InputStreamReader(is, charset);) { String result1 = readAll(fr, BUFFER_SIZE); String result2 = readAll(isr, BUFFER_SIZE); - Assert.assertEquals(result1, result2); + assertEquals(result2, result1); } } diff --git a/test/jdk/java/io/FileWriter/ConstructorTest.java b/test/jdk/java/io/FileWriter/ConstructorTest.java index 691b52504f9..64a43395f51 100644 --- a/test/jdk/java/io/FileWriter/ConstructorTest.java +++ b/test/jdk/java/io/FileWriter/ConstructorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -31,15 +31,21 @@ import java.io.OutputStreamWriter; import java.io.Reader; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import java.util.stream.Stream; + +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @test * @bug 8183554 * @summary Test to verify the new Constructors that take a Charset. - * @run testng ConstructorTest + * @run junit ConstructorTest */ public class ConstructorTest { static String USER_DIR = System.getProperty("user.dir", "."); @@ -54,21 +60,19 @@ public class ConstructorTest { static final String TEST_STRING = "abc \u0100 \u0101 \u0555 \u07FD \u07FF"; static final int BUFFER_SIZE = 8192; - @DataProvider(name = "parameters") - public Object[][] getParameters() throws IOException { + public static Stream parameters() throws IOException { File file1 = new File(USER_DIR, "FileWriterTest1.txt"); File file2 = new File(USER_DIR, "FileWriterTest2.txt"); - return new Object[][]{ - {ConstructorType.STRING, file1, file2, StandardCharsets.UTF_8}, - {ConstructorType.FILE, file1, file2, StandardCharsets.UTF_8}, - {ConstructorType.STRING_APPEND, file1, file2, StandardCharsets.UTF_8}, - {ConstructorType.FILE_APPEND, file1, file2, StandardCharsets.UTF_8}, - {ConstructorType.STRING, file1, file2, StandardCharsets.ISO_8859_1}, - {ConstructorType.FILE, file1, file2, StandardCharsets.ISO_8859_1}, - {ConstructorType.STRING_APPEND, file1, file2, StandardCharsets.ISO_8859_1}, - {ConstructorType.FILE_APPEND, file1, file2, StandardCharsets.ISO_8859_1}, - }; + return Stream.of + (Arguments.of(ConstructorType.STRING, file1, file2, StandardCharsets.UTF_8), + Arguments.of(ConstructorType.FILE, file1, file2, StandardCharsets.UTF_8), + Arguments.of(ConstructorType.STRING_APPEND, file1, file2, StandardCharsets.UTF_8), + Arguments.of(ConstructorType.FILE_APPEND, file1, file2, StandardCharsets.UTF_8), + Arguments.of(ConstructorType.STRING, file1, file2, StandardCharsets.ISO_8859_1), + Arguments.of(ConstructorType.FILE, file1, file2, StandardCharsets.ISO_8859_1), + Arguments.of(ConstructorType.STRING_APPEND, file1, file2, StandardCharsets.ISO_8859_1), + Arguments.of(ConstructorType.FILE_APPEND, file1, file2, StandardCharsets.ISO_8859_1)); } /** @@ -82,7 +86,8 @@ public class ConstructorTest { * @param charset the charset * @throws IOException */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("parameters") void test(ConstructorType type, File file1, File file2, Charset charset) throws Exception { writeWithFileWriter(type, file1, TEST_STRING, charset); @@ -94,7 +99,7 @@ public class ConstructorTest { ) { String result1 = readAll(r1, BUFFER_SIZE); String result2 = readAll(r2, BUFFER_SIZE); - Assert.assertEquals(result1, result2); + assertEquals(result1, result2); } } diff --git a/test/jdk/java/io/InputStream/NullInputStream.java b/test/jdk/java/io/InputStream/NullInputStream.java index 74317822404..d911edab380 100644 --- a/test/jdk/java/io/InputStream/NullInputStream.java +++ b/test/jdk/java/io/InputStream/NullInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -21,232 +21,148 @@ * questions. */ -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; -import static org.testng.Assert.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @bug 4358774 6516099 8139206 - * @run testng NullInputStream + * @run junit NullInputStream * @summary Check for expected behavior of InputStream.nullInputStream(). */ public class NullInputStream { private static InputStream openStream; private static InputStream closedStream; - @BeforeClass + @BeforeAll public static void setup() { openStream = InputStream.nullInputStream(); closedStream = InputStream.nullInputStream(); - try { - closedStream.close(); - } catch (IOException e) { - fail("Unexpected IOException"); - } + assertDoesNotThrow(() -> closedStream.close()); } - @AfterClass + @AfterAll public static void closeStream() { - try { - openStream.close(); - } catch (IOException e) { - fail("Unexpected IOException"); - } + assertDoesNotThrow(() -> openStream.close()); } @Test - public static void testOpen() { + public void testOpen() { assertNotNull(openStream, "InputStream.nullInputStream() returned null"); } @Test - public static void testAvailable() { - try { - assertEquals(0, openStream.available(), "available() != 0"); - } catch (IOException ioe) { - fail("Unexpected IOException"); - } + public void testAvailable() throws IOException { + assertEquals(0, openStream.available()); } @Test - public static void testRead() { - try { - assertEquals(-1, openStream.read(), "read() != -1"); - } catch (IOException ioe) { - fail("Unexpected IOException"); - } + public void testRead() throws IOException { + assertEquals(-1, openStream.read()); } @Test - public static void testReadBII() { - try { - assertEquals(-1, openStream.read(new byte[1], 0, 1), - "read(byte[],int,int) != -1"); - } catch (IOException ioe) { - fail("Unexpected IOException"); - } + public void testReadBII() throws IOException { + assertEquals(-1, openStream.read(new byte[1], 0, 1)); } @Test - public static void testReadAllBytes() { - try { - assertEquals(0, openStream.readAllBytes().length, - "readAllBytes().length != 0"); - } catch (IOException ioe) { - fail("Unexpected IOException"); - } + public void testReadAllBytes() throws IOException { + assertEquals(0, openStream.readAllBytes().length); } @Test - public static void testReadNBytes() { - try { - assertEquals(0, openStream.readNBytes(new byte[1], 0, 1), - "readNBytes(byte[],int,int) != 0"); - } catch (IOException ioe) { - fail("Unexpected IOException"); - } + public void testReadNBytes() throws IOException { + assertEquals(0, openStream.readNBytes(new byte[1], 0, 1)); } @Test - public static void testReadNBytesWithLength() { - try { - assertEquals(0, openStream.readNBytes(-1).length, - "readNBytes(-1) != 0"); - fail("Expected IllegalArgumentException not thrown"); - } catch (IllegalArgumentException iae) { - } catch (IOException ioe) { - fail("Unexpected IOException"); - } - try { - assertEquals(0, openStream.readNBytes(0).length, - "readNBytes(0, false) != 0"); - assertEquals(0, openStream.readNBytes(1).length, - "readNBytes(1, false) != 0"); - } catch (IOException ioe) { - fail("Unexpected IOException"); - } + public void testReadNBytesWithLength() throws IOException { + assertThrows(IllegalArgumentException.class, + () -> openStream.readNBytes(-1)); + assertEquals(0, openStream.readNBytes(0).length); + assertEquals(0, openStream.readNBytes(1).length); } @Test - public static void testSkip() { - try { - assertEquals(0, openStream.skip(1), "skip() != 0"); - } catch (IOException ioe) { - fail("Unexpected IOException"); - } + public void testSkip() throws IOException { + assertEquals(0L, openStream.skip(1)); } @Test - public static void testSkipNBytes() { - try { - openStream.skipNBytes(-1); - openStream.skipNBytes(0); - } catch (IOException ioe) { - fail("Unexpected IOException"); - } - } - - @Test(expectedExceptions = EOFException.class) - public static void testSkipNBytesEOF() throws IOException { - openStream.skipNBytes(1); + public void testSkipNBytes() { + assertDoesNotThrow(() -> { + openStream.skipNBytes(-1); + openStream.skipNBytes(0); + }); } @Test - public static void testTransferTo() { - try { - assertEquals(0, openStream.transferTo(new ByteArrayOutputStream(7)), - "transferTo() != 0"); - } catch (IOException ioe) { - fail("Unexpected IOException"); - } + public void testSkipNBytesEOF() throws IOException { + assertThrows(EOFException.class, () -> openStream.skipNBytes(1)); } @Test - public static void testAvailableClosed() { - try { - closedStream.available(); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testTransferTo() throws IOException { + assertEquals(0L, openStream.transferTo(new ByteArrayOutputStream(7))); } @Test - public static void testReadClosed() { - try { - closedStream.read(); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testAvailableClosed() { + assertThrows(IOException.class, () -> closedStream.available()); } @Test - public static void testReadBIIClosed() { - try { - closedStream.read(new byte[1], 0, 1); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testReadClosed() { + assertThrows(IOException.class, () -> closedStream.read()); } @Test - public static void testReadAllBytesClosed() { - try { - closedStream.readAllBytes(); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testReadBIIClosed() { + assertThrows(IOException.class, + () -> closedStream.read(new byte[1], 0, 1)); } @Test - public static void testReadNBytesClosed() { - try { - closedStream.readNBytes(new byte[1], 0, 1); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testReadAllBytesClosed() { + assertThrows(IOException.class, () -> closedStream.readAllBytes()); } @Test - public static void testReadNBytesWithLengthClosed() { - try { - closedStream.readNBytes(1); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testReadNBytesClosed() { + assertThrows(IOException.class, () -> + closedStream.readNBytes(new byte[1], 0, 1)); } @Test - public static void testSkipClosed() { - try { - closedStream.skip(1); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testReadNBytesWithLengthClosed() { + assertThrows(IOException.class, () -> closedStream.readNBytes(1)); } @Test - public static void testSkipNBytesClosed() { - try { - closedStream.skipNBytes(1); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testSkipClosed() { + assertThrows(IOException.class, () -> closedStream.skip(1)); } @Test - public static void testTransferToClosed() { - try { - closedStream.transferTo(new ByteArrayOutputStream(7)); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testSkipNBytesClosed() { + assertThrows(IOException.class, () -> closedStream.skipNBytes(1)); + } + + @Test + public void testTransferToClosed() { + assertThrows(IOException.class, + () -> closedStream.transferTo(new ByteArrayOutputStream(7))); } } diff --git a/test/jdk/java/io/InputStreamReader/ReadCharBuffer.java b/test/jdk/java/io/InputStreamReader/ReadCharBuffer.java index 8a4094c512a..6df3feb756f 100644 --- a/test/jdk/java/io/InputStreamReader/ReadCharBuffer.java +++ b/test/jdk/java/io/InputStreamReader/ReadCharBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -25,13 +25,9 @@ * @test * @bug 4926314 8287003 * @summary Test for InputStreamReader#read(CharBuffer). - * @run testng ReadCharBuffer + * @run junit ReadCharBuffer */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - - import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; @@ -40,22 +36,25 @@ import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CodingErrorAction; import java.util.Arrays; +import java.util.stream.Stream; import static java.nio.charset.StandardCharsets.US_ASCII; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class ReadCharBuffer { private static final int BUFFER_SIZE = 24; - @DataProvider(name = "buffers") - public Object[][] createBuffers() { + public static Stream buffers() { // test both on-heap and off-heap buffers as they make use different code paths - return new Object[][]{ - new Object[]{CharBuffer.allocate(BUFFER_SIZE)}, - new Object[]{ByteBuffer.allocateDirect(BUFFER_SIZE * 2).asCharBuffer()} - }; + return Stream.of(CharBuffer.allocate(BUFFER_SIZE), + ByteBuffer.allocateDirect(BUFFER_SIZE * 2).asCharBuffer()); } private void fillBuffer(CharBuffer buffer) { @@ -65,7 +64,8 @@ public class ReadCharBuffer { buffer.clear(); } - @Test(dataProvider = "buffers") + @ParameterizedTest + @MethodSource("buffers") public void read(CharBuffer buffer) throws IOException { fillBuffer(buffer); diff --git a/test/jdk/java/io/InputStreamReader/StatefulDecoderNearEOF.java b/test/jdk/java/io/InputStreamReader/StatefulDecoderNearEOF.java index 95759df5522..f456d176e98 100644 --- a/test/jdk/java/io/InputStreamReader/StatefulDecoderNearEOF.java +++ b/test/jdk/java/io/InputStreamReader/StatefulDecoderNearEOF.java @@ -23,7 +23,7 @@ /* @test * @bug 8292043 - * @run testng StatefulDecoderNearEOF + * @run junit StatefulDecoderNearEOF * @summary Check MalformedInputException is thrown with stateful decoders * with malformed input before EOF */ @@ -36,29 +36,30 @@ import java.nio.charset.CodingErrorAction; import java.nio.charset.MalformedInputException; import java.nio.charset.StandardCharsets; import java.util.stream.IntStream; +import java.util.stream.Stream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertThrows; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; -@Test public class StatefulDecoderNearEOF { - @DataProvider - public Object[][] inputs() { - return new Object[][] { + public static Stream inputs() { + return Stream.of( // BOM, followed by High surrogate (in UTF-16LE). // First read() should throw an exception. - {new byte[] {(byte)0xff, (byte)0xfe, 0, (byte)0xd8}, 0}, + Arguments.of(new byte[] {(byte)0xff, (byte)0xfe, 0, (byte)0xd8}, 0), // BOM, followed by 'A', 'B', 'C', then by High surrogate (in UTF-16LE). // Fourth read() should throw an exception. - {new byte[] {(byte)0xff, (byte)0xfe, (byte)0x41, 0, (byte)0x42, 0, (byte)0x43, 0, 0, (byte)0xd8}, 3}, - }; + Arguments.of(new byte[] {(byte)0xff, (byte)0xfe, (byte)0x41, 0, (byte)0x42, 0, (byte)0x43, 0, 0, (byte)0xd8}, 3)); } - @Test (dataProvider = "inputs") + @ParameterizedTest + @MethodSource("inputs") public void testStatefulDecoderNearEOF(byte[] ba, int numSucessReads) throws IOException { try (var r = new InputStreamReader( new ByteArrayInputStream(ba), diff --git a/test/jdk/java/io/LineNumberReader/MarkSplitCRLF.java b/test/jdk/java/io/LineNumberReader/MarkSplitCRLF.java index d735e78f684..187bef037c5 100644 --- a/test/jdk/java/io/LineNumberReader/MarkSplitCRLF.java +++ b/test/jdk/java/io/LineNumberReader/MarkSplitCRLF.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -24,7 +24,7 @@ /* @test * @bug 8218280 * @summary Make sure marking a line feed within a CRLF sequence works correctly - * @run testng MarkSplitCRLF + * @run junit MarkSplitCRLF */ import java.io.IOException; @@ -32,13 +32,13 @@ import java.io.LineNumberReader; import java.io.Reader; import java.io.StringReader; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; public class MarkSplitCRLF { @Test - public static void testSpecifiedBufferSize() throws IOException { + public void testSpecifiedBufferSize() throws IOException { final String string = "foo\r\nbar"; try (Reader reader = new LineNumberReader(new StringReader(string), 5)) { @@ -56,7 +56,7 @@ public class MarkSplitCRLF { } @Test - public static void testCRNotFollowedByLF() throws IOException { + public void testCRNotFollowedByLF() throws IOException { final String string = "foo\rbar"; try (Reader reader = new LineNumberReader(new StringReader(string), 5)) { @@ -74,7 +74,7 @@ public class MarkSplitCRLF { } @Test - public static void testDefaultBufferSize() throws IOException { + public void testDefaultBufferSize() throws IOException { StringBuilder sb = new StringBuilder(8195); for (int i = 0; i < 8190; i++) { char c = (char)i; diff --git a/test/jdk/java/io/ObjectStreamClass/ObjectStreamClassCaching.java b/test/jdk/java/io/ObjectStreamClass/ObjectStreamClassCaching.java index 4004cbcf859..b1765b04060 100644 --- a/test/jdk/java/io/ObjectStreamClass/ObjectStreamClassCaching.java +++ b/test/jdk/java/io/ObjectStreamClass/ObjectStreamClassCaching.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -26,9 +26,10 @@ import java.lang.ref.WeakReference; import java.io.ObjectStreamClass; import java.io.Serializable; import java.util.ArrayList; -import org.testng.annotations.Test; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test id=G1 @@ -36,7 +37,7 @@ import static org.testng.Assert.assertTrue; * @bug 8277072 * @library /test/lib/ * @summary ObjectStreamClass caches keep ClassLoaders alive (G1 GC) - * @run testng/othervm -Xmx64m -XX:+UseG1GC ObjectStreamClassCaching + * @run junit/othervm -Xmx64m -XX:+UseG1GC ObjectStreamClassCaching */ /* @@ -45,7 +46,7 @@ import static org.testng.Assert.assertTrue; * @bug 8277072 * @library /test/lib/ * @summary ObjectStreamClass caches keep ClassLoaders alive (Parallel GC) - * @run testng/othervm -Xmx64m -XX:+UseParallelGC ObjectStreamClassCaching + * @run junit/othervm -Xmx64m -XX:+UseParallelGC ObjectStreamClassCaching */ /* @@ -54,7 +55,7 @@ import static org.testng.Assert.assertTrue; * @bug 8277072 8327180 * @library /test/lib/ * @summary ObjectStreamClass caches keep ClassLoaders alive (ZGC) - * @run testng/othervm -Xmx64m -XX:+UseZGC ObjectStreamClassCaching + * @run junit/othervm -Xmx64m -XX:+UseZGC ObjectStreamClassCaching */ /* @@ -63,7 +64,7 @@ import static org.testng.Assert.assertTrue; * @bug 8277072 * @library /test/lib/ * @summary ObjectStreamClass caches keep ClassLoaders alive (Shenandoah GC) - * @run testng/othervm -Xmx64m -XX:+UseShenandoahGC ObjectStreamClassCaching + * @run junit/othervm -Xmx64m -XX:+UseShenandoahGC ObjectStreamClassCaching */ /* @@ -72,7 +73,7 @@ import static org.testng.Assert.assertTrue; * @bug 8277072 8327180 * @library /test/lib/ * @summary ObjectStreamClass caches keep ClassLoaders alive (Serial GC) - * @run testng/othervm -Xmx64m -XX:+UseSerialGC ObjectStreamClassCaching + * @run junit/othervm -Xmx64m -XX:+UseSerialGC ObjectStreamClassCaching */ public class ObjectStreamClassCaching { diff --git a/test/jdk/java/io/ObjectStreamClass/TestOSCClassLoaderLeak.java b/test/jdk/java/io/ObjectStreamClass/TestOSCClassLoaderLeak.java index 1816aa45fe7..e7b944f6574 100644 --- a/test/jdk/java/io/ObjectStreamClass/TestOSCClassLoaderLeak.java +++ b/test/jdk/java/io/ObjectStreamClass/TestOSCClassLoaderLeak.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -29,18 +29,20 @@ import java.io.ObjectStreamClass; import java.io.ObjectStreamField; import java.io.Serializable; import java.util.Arrays; -import org.testng.annotations.Test; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertFalse; import jdk.test.lib.util.ForceGC; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + /* @test * @bug 8277072 * @library /test/lib/ * @build jdk.test.lib.util.ForceGC * @summary ObjectStreamClass caches keep ClassLoaders alive - * @run testng TestOSCClassLoaderLeak + * @run junit TestOSCClassLoaderLeak */ public class TestOSCClassLoaderLeak { diff --git a/test/jdk/java/io/OutputStream/NullOutputStream.java b/test/jdk/java/io/OutputStream/NullOutputStream.java index e0041e424d8..d0f76596db3 100644 --- a/test/jdk/java/io/OutputStream/NullOutputStream.java +++ b/test/jdk/java/io/OutputStream/NullOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -21,85 +21,64 @@ * questions. */ -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - import java.io.IOException; import java.io.OutputStream; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.fail; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @bug 4358774 - * @run testng NullOutputStream + * @run junit NullOutputStream * @summary Check for expected behavior of OutputStream.nullOutputStream(). */ public class NullOutputStream { private static OutputStream openStream; private static OutputStream closedStream; - @BeforeClass + @BeforeAll public static void setup() { openStream = OutputStream.nullOutputStream(); closedStream = OutputStream.nullOutputStream(); - try { - closedStream.close(); - } catch (IOException e) { - fail("Unexpected IOException"); - } + assertDoesNotThrow(() -> closedStream.close()); } - @AfterClass + @AfterAll public static void closeStream() { - try { - openStream.close(); - } catch (IOException e) { - fail("Unexpected IOException"); - } + assertDoesNotThrow(() -> openStream.close()); } @Test - public static void testOpen() { + public void testOpen() { assertNotNull(openStream, "OutputStream.nullOutputStream() returned null"); } @Test - public static void testWrite() { - try { - openStream.write(62832); - } catch (IOException e) { - fail("Unexpected IOException"); - } + public void testWrite() throws IOException { + openStream.write(62832); } @Test - public static void testWriteBII() { - try { - openStream.write(new byte[] {(byte)6}, 0, 1); - } catch (Exception e) { - fail("Unexpected IOException"); - } + public void testWriteBII() { + assertDoesNotThrow(() -> openStream.write(new byte[] {(byte)6}, 0, 1)); } @Test - public static void testWriteClosed() { - try { - closedStream.write(62832); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testWriteClosed() { + assertThrows(IOException.class, () -> closedStream.write(62832)); } @Test - public static void testWriteBIIClosed() { - try { - closedStream.write(new byte[] {(byte)6}, 0, 1); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testWriteBIIClosed() { + assertThrows(IOException.class, + () -> closedStream.write(new byte[] {(byte)6}, 0, 1)); } } diff --git a/test/jdk/java/io/PrintStream/EncodingTest.java b/test/jdk/java/io/PrintStream/EncodingTest.java index d767b5801d3..a0fe27b81cb 100644 --- a/test/jdk/java/io/PrintStream/EncodingTest.java +++ b/test/jdk/java/io/PrintStream/EncodingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -29,16 +29,20 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @test * @bug 8183743 * @summary Test to verify the new overload method with Charset functions the same * as the existing method that takes a charset name. - * @run testng EncodingTest + * @run junit EncodingTest */ public class EncodingTest { static String USER_DIR = System.getProperty("user.dir", "."); @@ -50,22 +54,20 @@ public class EncodingTest { } /* - * DataProvider fields: + * MethodSource fields: * Type of the constructor, a file to be written with a charset name, * a file to be written with a charset, charset name, charset. */ - @DataProvider(name = "parameters") - public Object[][] getParameters() throws IOException { + public static Stream parameters() throws IOException { String csn = StandardCharsets.UTF_8.name(); Charset charset = StandardCharsets.UTF_8; File file1 = new File(USER_DIR, "PSCharsetTest1.txt"); File file2 = new File(USER_DIR, "PSCharsetTest2.txt"); - return new Object[][]{ - {ConstructorType.STRING, file1, file2, csn, charset}, - {ConstructorType.FILE, file1, file2, csn, charset}, - {ConstructorType.OUTPUTSTREAM, file1, file2, csn, charset} - }; + return Stream.of + (Arguments.of(ConstructorType.STRING, file1, file2, csn, charset), + Arguments.of(ConstructorType.FILE, file1, file2, csn, charset), + Arguments.of(ConstructorType.OUTPUTSTREAM, file1, file2, csn, charset)); } /** @@ -78,14 +80,15 @@ public class EncodingTest { * @param charset the charset * @throws IOException */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("parameters") public void test(ConstructorType type, File file1, File file2, String csn, Charset charset) throws Exception { createFile(getPrintStream(type, file1.getPath(), csn, null)); createFile(getPrintStream(type, file2.getPath(), null, charset)); - Assert.assertEquals(Files.readAllLines(Paths.get(file1.getPath()), charset), - Files.readAllLines(Paths.get(file2.getPath()), charset)); + assertEquals(Files.readAllLines(Paths.get(file2.getPath()), charset), + Files.readAllLines(Paths.get(file1.getPath()), charset)); } public void createFile(PrintStream out) throws IOException { diff --git a/test/jdk/java/io/PrintStream/InheritEncodingTest.java b/test/jdk/java/io/PrintStream/InheritEncodingTest.java index e31404114a0..094a1d4e0be 100644 --- a/test/jdk/java/io/PrintStream/InheritEncodingTest.java +++ b/test/jdk/java/io/PrintStream/InheritEncodingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -21,10 +21,6 @@ * questions. */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; @@ -33,31 +29,35 @@ import java.io.PrintWriter; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; + /** * @test * @bug 8276970 * @summary Test to verify the charset in PrintStream is inherited * in the OutputStreamWriter/PrintWriter - * @run testng InheritEncodingTest + * @run junit InheritEncodingTest */ -@Test public class InheritEncodingTest { private static final String testString = "\u00e9\u3042"; // "éあ" - @DataProvider - public Object[][] encodings() { - return new Object[][]{ - {StandardCharsets.ISO_8859_1}, - {StandardCharsets.US_ASCII}, - {StandardCharsets.UTF_8}, - {StandardCharsets.UTF_16}, - {StandardCharsets.UTF_16BE}, - {StandardCharsets.UTF_16LE}, + public static Charset[] encodings() { + return new Charset[]{ + StandardCharsets.ISO_8859_1, + StandardCharsets.US_ASCII, + StandardCharsets.UTF_8, + StandardCharsets.UTF_16, + StandardCharsets.UTF_16BE, + StandardCharsets.UTF_16LE }; } - @Test (dataProvider = "encodings") + @ParameterizedTest + @MethodSource("encodings") public void testOutputStreamWriter(Charset stdCharset) throws IOException { var ba = new ByteArrayOutputStream(); var ps = new PrintStream(ba, true, stdCharset); @@ -65,16 +65,17 @@ public class InheritEncodingTest { // tests OutputStreamWriter's encoding explicitly var osw = new OutputStreamWriter(ps); - assertEquals(Charset.forName(osw.getEncoding()), stdCharset); + assertEquals(stdCharset, Charset.forName(osw.getEncoding())); // tests roundtrip result osw.write(testString); osw.flush(); var result = ba.toString(stdCharset); - assertEquals(result, expected); + assertEquals(expected, result); } - @Test (dataProvider = "encodings") + @ParameterizedTest + @MethodSource("encodings") public void testPrintWriter(Charset stdCharset) throws IOException { var ba = new ByteArrayOutputStream(); var ps = new PrintStream(ba, true, stdCharset); @@ -85,10 +86,11 @@ public class InheritEncodingTest { pw.write(testString); pw.flush(); var result = ba.toString(stdCharset); - assertEquals(result, expected); + assertEquals(expected, result); } - @Test (dataProvider = "encodings") + @ParameterizedTest + @MethodSource("encodings") public void testPrintStream(Charset stdCharset) throws IOException { var ba = new ByteArrayOutputStream(); var ps = new PrintStream(ba, true, stdCharset); @@ -96,12 +98,12 @@ public class InheritEncodingTest { // tests PrintStream's charset explicitly var psWrapper = new PrintStream(ps); - assertEquals(psWrapper.charset(), stdCharset); + assertEquals(stdCharset, psWrapper.charset()); // tests roundtrip result psWrapper.print(testString); psWrapper.flush(); var result = ba.toString(stdCharset); - assertEquals(result, expected); + assertEquals(expected, result); } } diff --git a/test/jdk/java/io/PrintWriter/EncodingTest.java b/test/jdk/java/io/PrintWriter/EncodingTest.java index f02944d9900..ef6e8b0094b 100644 --- a/test/jdk/java/io/PrintWriter/EncodingTest.java +++ b/test/jdk/java/io/PrintWriter/EncodingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -29,16 +29,20 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @test * @bug 8183743 * @summary Test to verify the new overload method with Charset functions the same * as the existing method that takes a charset name. - * @run testng EncodingTest + * @run junit EncodingTest */ public class EncodingTest { static String USER_DIR = System.getProperty("user.dir", "."); @@ -54,18 +58,16 @@ public class EncodingTest { * Type of the constructor, a file to be written with a charset name, * a file to be written with a charset, charset name, charset. */ - @DataProvider(name = "parameters") - public Object[][] getParameters() throws IOException { + public static Stream parameters() throws IOException { String csn = StandardCharsets.UTF_8.name(); Charset charset = StandardCharsets.UTF_8; File file1 = new File(USER_DIR, "PWCharsetTest1.txt"); File file2 = new File(USER_DIR, "PWCharsetTest2.txt"); - return new Object[][]{ - {ConstructorType.STRING, file1, file2, csn, charset}, - {ConstructorType.FILE, file1, file2, csn, charset}, - {ConstructorType.OUTPUTSTREAM, file1, file2, csn, charset} - }; + return Stream.of + (Arguments.of(ConstructorType.STRING, file1, file2, csn, charset), + Arguments.of(ConstructorType.FILE, file1, file2, csn, charset), + Arguments.of(ConstructorType.OUTPUTSTREAM, file1, file2, csn, charset)); } /** @@ -79,14 +81,15 @@ public class EncodingTest { * @param charset the charset * @throws IOException */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("parameters") public void test(ConstructorType type, File file1, File file2, String csn, Charset charset) throws Exception { createFile(getWriter(type, file1.getPath(), csn, null)); createFile(getWriter(type, file2.getPath(), null, charset)); - Assert.assertEquals(Files.readAllLines(Paths.get(file1.getPath()), charset), - Files.readAllLines(Paths.get(file2.getPath()), charset)); + assertEquals(Files.readAllLines(Paths.get(file2.getPath()), charset), + Files.readAllLines(Paths.get(file1.getPath()), charset)); } void createFile(PrintWriter out) throws IOException { diff --git a/test/jdk/java/io/PushbackInputStream/TransferTo.java b/test/jdk/java/io/PushbackInputStream/TransferTo.java index 0a27d88bc70..5d601748003 100644 --- a/test/jdk/java/io/PushbackInputStream/TransferTo.java +++ b/test/jdk/java/io/PushbackInputStream/TransferTo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -32,21 +32,21 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Supplier; -import org.testng.annotations.Test; +import static java.lang.String.format; import jdk.test.lib.RandomFactory; -import static java.lang.String.format; +import org.junit.jupiter.api.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @library /test/lib * @build jdk.test.lib.RandomFactory - * @run testng/othervm/timeout=180 TransferTo + * @run junit/othervm/timeout=180 TransferTo * @bug 8296431 * @summary Tests whether java.io.PushbackInputStream.transferTo conforms to the * InputStream.transferTo specification @@ -162,7 +162,7 @@ public class TransferTo { long reported = in.transferTo(out); int count = inBytes.length - posIn; - assertEquals(reported, count, + assertEquals(count, reported, format("reported %d bytes but should report %d", reported, count)); byte[] outBytes = recorder.get().get(); diff --git a/test/jdk/java/io/Reader/NullReader.java b/test/jdk/java/io/Reader/NullReader.java index 9c16f33df1c..b80c1a9f67a 100644 --- a/test/jdk/java/io/Reader/NullReader.java +++ b/test/jdk/java/io/Reader/NullReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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,118 +27,125 @@ import java.io.StringWriter; import java.nio.CharBuffer; import java.nio.ReadOnlyBufferException; -import org.testng.annotations.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @bug 8196298 8204930 - * @run testng NullReader + * @run junit NullReader * @summary Check for expected behavior of Reader.nullReader(). */ public class NullReader { private static Reader openReader; private static Reader closedReader; - @BeforeClass + @BeforeAll public static void setup() throws IOException { openReader = Reader.nullReader(); closedReader = Reader.nullReader(); closedReader.close(); } - @AfterClass + @AfterAll public static void closeStream() throws IOException { openReader.close(); } @Test - public static void testOpen() { + public void testOpen() { assertNotNull(openReader, "Reader.nullReader() returned null"); } @Test - public static void testRead() throws IOException { + public void testRead() throws IOException { assertEquals(-1, openReader.read(), "read() != -1"); } @Test - public static void testReadBII() throws IOException { + public void testReadBII() throws IOException { assertEquals(-1, openReader.read(new char[1], 0, 1), "read(char[],int,int) != -1"); } @Test - public static void testReadBIILenZero() throws IOException { + public void testReadBIILenZero() throws IOException { assertEquals(0, openReader.read(new char[1], 0, 0), "read(char[],int,int) != 0"); } @Test - public static void testReadCharBuffer() throws IOException { + public void testReadCharBuffer() throws IOException { CharBuffer charBuffer = CharBuffer.allocate(1); assertEquals(-1, openReader.read(charBuffer), "read(CharBuffer) != -1"); } @Test - public static void testReadCharBufferZeroRemaining() throws IOException { + public void testReadCharBufferZeroRemaining() throws IOException { CharBuffer charBuffer = CharBuffer.allocate(0); assertEquals(0, openReader.read(charBuffer), "read(CharBuffer) != 0"); } @Test - public static void testReady() throws IOException { + public void testReady() throws IOException { assertFalse(openReader.ready()); } @Test - public static void testSkip() throws IOException { + public void testSkip() throws IOException { assertEquals(0, openReader.skip(1), "skip() != 0"); } @Test - public static void testTransferTo() throws IOException { + public void testTransferTo() throws IOException { assertEquals(0, openReader.transferTo(new StringWriter(7)), "transferTo() != 0"); } - @Test(expectedExceptions = IOException.class) - public static void testReadClosed() throws IOException { - closedReader.read(); + @Test + public void testReadClosed() throws IOException { + assertThrows(IOException.class, () -> closedReader.read()); } - @Test(expectedExceptions = IOException.class) - public static void testReadBIIClosed() throws IOException { - closedReader.read(new char[1], 0, 1); + @Test + public void testReadBIIClosed() throws IOException { + assertThrows(IOException.class, + () -> closedReader.read(new char[1], 0, 1)); } - @Test(expectedExceptions = IOException.class) - public static void testReadCharBufferClosed() throws IOException { + @Test + public void testReadCharBufferClosed() throws IOException { CharBuffer charBuffer = CharBuffer.allocate(0); - closedReader.read(charBuffer); + assertThrows(IOException.class, () -> closedReader.read(charBuffer)); } - @Test(expectedExceptions = IOException.class) - public static void testReadCharBufferZeroRemainingClosed() throws IOException { + @Test + public void testReadCharBufferZeroRemainingClosed() throws IOException { CharBuffer charBuffer = CharBuffer.allocate(0); - closedReader.read(charBuffer); + assertThrows(IOException.class, () -> closedReader.read(charBuffer)); } - @Test(expectedExceptions = IOException.class) - public static void testReadyClosed() throws IOException { - closedReader.ready(); + @Test + public void testReadyClosed() throws IOException { + assertThrows(IOException.class, () -> closedReader.ready()); } - @Test(expectedExceptions = IOException.class) - public static void testSkipClosed() throws IOException { - closedReader.skip(1); + @Test + public void testSkipClosed() throws IOException { + assertThrows(IOException.class, () -> closedReader.skip(1)); } - @Test(expectedExceptions = IOException.class) - public static void testTransferToClosed() throws IOException { - closedReader.transferTo(new StringWriter(7)); + @Test + public void testTransferToClosed() throws IOException { + assertThrows(IOException.class, + () -> closedReader.transferTo(new StringWriter(7))); } } diff --git a/test/jdk/java/io/Reader/Of.java b/test/jdk/java/io/Reader/Of.java index 491c0499e6b..7d3e039b920 100644 --- a/test/jdk/java/io/Reader/Of.java +++ b/test/jdk/java/io/Reader/Of.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -29,15 +29,18 @@ import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.ReadOnlyBufferException; -import org.testng.annotations.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @bug 8341566 * @summary Check for expected behavior of Reader.of(). - * @run testng Of + * @run junit Of */ public class Of { final static String CONTENT = "Some Reader Test"; @@ -45,7 +48,6 @@ public class Of { /* * Readers to be tested. */ - @DataProvider public static Reader[] readers() { return new Reader[] { new StringReader(CONTENT), @@ -81,135 +83,152 @@ public class Of { }; } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testRead(Reader reader) throws IOException { String s = ""; for (int c; (c = reader.read()) != -1; s += (char) c); - assertEquals(s, CONTENT, "read() returned wrong value"); + assertEquals(CONTENT, s, "read() returned wrong value"); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadBII(Reader reader) throws IOException { char[] c = new char[16]; - assertEquals(reader.read(c, 8, 8), 8, + assertEquals(8, reader.read(c, 8, 8), "read(char[],int,int) does not respect given start or end"); - assertEquals(reader.read(c, 0, 16), 8, + assertEquals(8, reader.read(c, 0, 16), "read(char[],int,int) does not respect end of stream"); - assertEquals(new String(c), - CONTENT.substring(8, 16) + CONTENT.substring(0, 8), + assertEquals(CONTENT.substring(8, 16) + CONTENT.substring(0, 8), + new String(c), "read(char[],int,int) provides wrong content"); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadBIILenZero(Reader reader) throws IOException { - assertEquals(reader.read(new char[1], 0, 0), 0, + assertEquals(0, reader.read(new char[1], 0, 0), "read(char[],int,int) != 0"); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadDirectCharBuffer(Reader reader) throws IOException { CharBuffer charBuffer = ByteBuffer.allocateDirect(32).asCharBuffer(); charBuffer.position(8); - assertEquals(reader.read(charBuffer), 8, + assertEquals(8, reader.read(charBuffer), "read(CharBuffer) does not respect position or limit"); charBuffer.rewind(); - assertEquals(reader.read(charBuffer), 8, + assertEquals(8, reader.read(charBuffer), "read(CharBuffer) does not respect end of stream"); charBuffer.rewind(); - assertEquals(charBuffer.toString(), - // last part first proofs that copy loops correctly stopped - CONTENT.substring(8, 16) + CONTENT.substring(0, 8), + // last part first proves that copy loops correctly stopped + assertEquals(CONTENT.substring(8, 16) + CONTENT.substring(0, 8), + charBuffer.toString(), "read(CharBuffer) provides wrong content"); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadNonDirectCharBuffer(Reader reader) throws IOException { CharBuffer charBuffer = CharBuffer.allocate(16); charBuffer.position(8); - assertEquals(reader.read(charBuffer), 8, + assertEquals(8, reader.read(charBuffer), "read(CharBuffer) does not respect position or limit"); charBuffer.rewind(); - assertEquals(reader.read(charBuffer), 8, + assertEquals(8, reader.read(charBuffer), "read(CharBuffer) does not respect end of stream"); charBuffer.rewind(); - assertEquals(charBuffer.toString(), - CONTENT.substring(8, 16) + CONTENT.substring(0, 8), + assertEquals(CONTENT.substring(8, 16) + CONTENT.substring(0, 8), + charBuffer.toString(), "read(CharBuffer) provides wrong content"); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadCharBufferZeroRemaining(Reader reader) throws IOException { CharBuffer charBuffer = CharBuffer.allocate(0); - assertEquals(reader.read(charBuffer), 0, "read(CharBuffer) != 0"); + assertEquals(0, reader.read(charBuffer), "read(CharBuffer) != 0"); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReady(Reader reader) throws IOException { assertTrue(reader.ready()); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testSkip(Reader reader) throws IOException { - assertEquals(reader.skip(8), 8, "skip() does not respect limit"); - assertEquals(reader.skip(9), 8, "skip() does not respect end of stream"); - assertEquals(reader.skip(1), 0, "skip() does not respect empty stream"); + assertEquals(8, reader.skip(8), "skip() does not respect limit"); + assertEquals(8, reader.skip(9), "skip() does not respect end of stream"); + assertEquals(0, reader.skip(1), "skip() does not respect empty stream"); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testTransferTo(Reader reader) throws IOException { StringWriter sw = new StringWriter(16); - assertEquals(reader.transferTo(sw), 16, "transferTo() != 16"); - assertEquals(reader.transferTo(sw), 0, + assertEquals(16, reader.transferTo(sw), "transferTo() != 16"); + assertEquals(0, reader.transferTo(sw), "transferTo() does not respect empty stream"); - assertEquals(sw.toString(), CONTENT, + assertEquals(CONTENT, sw.toString(), "transferTo() provides wrong content"); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadClosed(Reader reader) throws IOException { reader.close(); assertThrows(IOException.class, () -> {reader.read();}); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadBIIClosed(Reader reader) throws IOException { reader.close(); assertThrows(IOException.class, () -> reader.read(new char[1], 0, 1)); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadCharBufferClosed(Reader reader) throws IOException { CharBuffer charBuffer = CharBuffer.allocate(1); reader.close(); assertThrows(IOException.class, () -> reader.read(charBuffer)); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadCharBufferZeroRemainingClosed(Reader reader) throws IOException { CharBuffer charBuffer = CharBuffer.allocate(0); reader.close(); assertThrows(IOException.class, () -> reader.read(charBuffer)); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadyClosed(Reader reader) throws IOException { reader.close(); assertThrows(IOException.class, () -> reader.ready()); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testSkipClosed(Reader reader) throws IOException { reader.close(); assertThrows(IOException.class, () -> reader.skip(1)); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testTransferToClosed(Reader reader) throws IOException { reader.close(); assertThrows(IOException.class, () -> reader.transferTo(new StringWriter(1))); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testCloseClosed(Reader reader) throws IOException { reader.close(); reader.close(); diff --git a/test/jdk/java/io/Reader/ReadCharBuffer.java b/test/jdk/java/io/Reader/ReadCharBuffer.java index c57860601fb..dc858999287 100644 --- a/test/jdk/java/io/Reader/ReadCharBuffer.java +++ b/test/jdk/java/io/Reader/ReadCharBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -25,40 +25,38 @@ * @test * @bug 4926314 8266014 * @summary Test for Reader#read(CharBuffer). - * @run testng ReadCharBuffer + * @run junit ReadCharBuffer */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - - -import java.io.IOException; import java.io.BufferedReader; import java.io.CharArrayReader; +import java.io.IOException; import java.io.Reader; import java.io.UncheckedIOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.util.Arrays; import java.util.Objects; +import java.util.stream.Stream; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; -@Test(groups = "unit") public class ReadCharBuffer { private static final int BUFFER_SIZE = 8 + 8192 + 2; - @DataProvider(name = "buffers") - public Object[][] createBuffers() { + public static Stream buffers() { // test both on-heap and off-heap buffers as they make use different code paths - return new Object[][]{ - new Object[]{CharBuffer.allocate(BUFFER_SIZE)}, - new Object[]{ByteBuffer.allocateDirect(BUFFER_SIZE * 2).asCharBuffer()} - }; + return Stream.of(CharBuffer.allocate(BUFFER_SIZE), + ByteBuffer.allocateDirect(BUFFER_SIZE * 2).asCharBuffer()); } - @Test(dataProvider = "buffers") + @ParameterizedTest + @MethodSource("buffers") public void read(CharBuffer buffer) throws IOException { fillBuffer(buffer); @@ -74,21 +72,21 @@ public class ReadCharBuffer { int limit = 1 + 6; buffer.limit(limit); buffer.position(1); - assertEquals(reader.read(buffer), 6); - assertEquals(buffer.position(), limit); - assertEquals(buffer.limit(), limit); + assertEquals(6, reader.read(buffer)); + assertEquals(limit, buffer.position()); + assertEquals(limit, buffer.limit()); // read the full temporary buffer // and then accurately reduce the next #read call limit = 8 + 8192 + 1; buffer.limit(8 + 8192 + 1); buffer.position(8); - assertEquals(reader.read(buffer), 8192 + 1); - assertEquals(buffer.position(), limit); - assertEquals(buffer.limit(), limit); + assertEquals(8192 + 1, reader.read(buffer)); + assertEquals(limit, buffer.position()); + assertEquals(limit, buffer.limit()); - assertEquals(reader.read(), 'H'); - assertEquals(reader.read(), -1); + assertEquals('H', reader.read()); + assertEquals(-1, reader.read()); } buffer.clear(); @@ -98,20 +96,15 @@ public class ReadCharBuffer { expected.append('y'); } expected.append("Gx"); - assertEquals(buffer.toString(), expected.toString()); + assertEquals(expected.toString(), buffer.toString()); } @Test - public void readZeroLength() { + public void readZeroLength() throws IOException { char[] buf = new char[] {1, 2, 3}; BufferedReader r = new BufferedReader(new CharArrayReader(buf)); - int n = -1; - try { - n = r.read(CharBuffer.allocate(0)); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - assertEquals(n, 0); + int n = r.read(CharBuffer.allocate(0)); + assertEquals(0, n); } private void fillBuffer(CharBuffer buffer) { diff --git a/test/jdk/java/io/Reader/ReadIntoZeroLengthArray.java b/test/jdk/java/io/Reader/ReadIntoZeroLengthArray.java index 480cd32c981..e69a24d38f4 100644 --- a/test/jdk/java/io/Reader/ReadIntoZeroLengthArray.java +++ b/test/jdk/java/io/Reader/ReadIntoZeroLengthArray.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -29,55 +29,57 @@ import java.io.LineNumberReader; import java.io.PushbackReader; import java.io.Reader; import java.io.StringReader; -import org.testng.Assert; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; /* * @test * @bug 8248383 * @summary Ensure that zero is returned for read into zero length array - * @run testng ReadIntoZeroLengthArray + * @run junit ReadIntoZeroLengthArray */ public class ReadIntoZeroLengthArray { - private File file; + private static File file; - private char[] cbuf0; - private char[] cbuf1; + private static char[] cbuf0; + private static char[] cbuf1; - @BeforeTest - public void setup() throws IOException { + @BeforeAll + public static void setup() throws IOException { file = File.createTempFile("foo", "bar", new File(".")); + cbuf0 = new char[0]; cbuf1 = new char[1]; } - @AfterTest - public void teardown() throws IOException { + @AfterAll + public static void teardown() throws IOException { file.delete(); } - @DataProvider(name = "readers") - public Object[][] getReaders() throws IOException { - Reader fileReader = new FileReader(file); - return new Object[][] { - {new LineNumberReader(fileReader)}, - {new CharArrayReader(new char[] {27})}, - {new PushbackReader(fileReader)}, - {fileReader}, - {new StringReader(new String(new byte[] {(byte)42}))} - }; + public static Stream readers() throws IOException { + return Stream.of(new LineNumberReader(new FileReader(file)), + new CharArrayReader(new char[] {27}), + new PushbackReader(new FileReader(file)), + new FileReader(file), + new StringReader(new String(new byte[] {(byte)42}))); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") void test0(Reader r) throws IOException { - Assert.assertEquals(r.read(cbuf0), 0); + assertEquals(0, r.read(cbuf0)); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") void test1(Reader r) throws IOException { - Assert.assertEquals(r.read(cbuf1, 0, 0), 0); + assertEquals(0, r.read(cbuf1, 0, 0)); } } diff --git a/test/jdk/java/io/Reader/Skip.java b/test/jdk/java/io/Reader/Skip.java index 9e311686507..80d260d4276 100644 --- a/test/jdk/java/io/Reader/Skip.java +++ b/test/jdk/java/io/Reader/Skip.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -24,7 +24,7 @@ /* @test * @bug 4134311 8247918 * @summary Test if skip works correctly - * @run testng Skip + * @run junit Skip */ import java.io.CharArrayReader; @@ -36,10 +36,16 @@ import java.io.PushbackReader; import java.io.RandomAccessFile; import java.io.Reader; import java.io.StringReader; +import java.util.stream.Stream; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; public class Skip { private static String FILENAME = @@ -52,52 +58,52 @@ public class Skip { long nchars = 8200; long actual = fr.skip(nchars); - Assert.assertFalse(actual > nchars, + assertFalse(actual > nchars, "Should skip " + nchars + ", but skipped " +actual+" chars"); } } - @DataProvider(name = "readers") - public Object[][] getReaders() throws IOException { - return new Object[][] { - {new LineNumberReader(new FileReader(file))}, - {new CharArrayReader(new char[] {27})}, - {new PushbackReader(new FileReader(file))}, - {new FileReader(file)}, - {new StringReader(new String(new byte[] {(byte)42}))} + public static Reader[] readers() throws IOException { + return new Reader[] { + new LineNumberReader(new FileReader(file)), + new CharArrayReader(new char[] {27}), + new PushbackReader(new FileReader(file)), + new FileReader(file), + new StringReader(new String(new byte[] {(byte)42})) }; } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void eof(Reader r) throws IOException { r.skip(Long.MAX_VALUE); - Assert.assertEquals(r.skip(1), 0); - Assert.assertEquals(r.read(), -1); + assertEquals(0, r.skip(1)); + assertEquals(-1, r.read()); } - @DataProvider(name = "skipIAE") - public Object[][] getSkipIAEs() throws IOException { - return new Object[][] { - {new LineNumberReader(new FileReader(file))}, - {new PushbackReader(new FileReader(file))}, - {new FileReader(file)} + public static Reader[] skipIAE() throws IOException { + return new Reader[] { + new LineNumberReader(new FileReader(file)), + new PushbackReader(new FileReader(file)), + new FileReader(file) }; } - @Test(dataProvider = "skipIAE", expectedExceptions = IllegalArgumentException.class) + @ParameterizedTest + @MethodSource("skipIAE") public void testThrowsIAE(Reader r) throws IOException { - r.skip(-1); + assertThrows(IllegalArgumentException.class, () -> r.skip(-1)); } - @DataProvider(name = "skipNoIAE") - public Object[][] getSkipNoIAEs() throws IOException { - return new Object[][] { - {new CharArrayReader(new char[] {27})}, - {new StringReader(new String(new byte[] {(byte)42}))} + public static Reader[] skipNoIAE() throws IOException { + return new Reader[] { + new CharArrayReader(new char[] {27}), + new StringReader(new String(new byte[] {(byte)42})) }; } - @Test(dataProvider = "skipNoIAE") + @ParameterizedTest + @MethodSource("skipNoIAE") public void testNoIAE(Reader r) throws IOException { r.skip(-1); } diff --git a/test/jdk/java/io/SequenceInputStream/TransferTo.java b/test/jdk/java/io/SequenceInputStream/TransferTo.java index 4c8ff71a4f3..8ea9a77672e 100644 --- a/test/jdk/java/io/SequenceInputStream/TransferTo.java +++ b/test/jdk/java/io/SequenceInputStream/TransferTo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -33,22 +33,22 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Supplier; -import org.testng.annotations.Test; +import static java.lang.String.format; import jdk.test.lib.RandomFactory; -import static java.lang.String.format; +import org.junit.jupiter.api.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotEquals; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @library /test/lib * @build jdk.test.lib.RandomFactory - * @run testng/othervm/timeout=180 TransferTo + * @run junit/othervm/timeout=180 TransferTo * @bug 8297298 * @summary Tests whether java.io.SequenceInputStream.transferTo conforms to the * InputStream.transferTo specification @@ -141,8 +141,8 @@ public class TransferTo { SequenceInputStream sis = new SequenceInputStream(is1, is2); OutputStream nos = OutputStream.nullOutputStream(); sis.transferTo(nos); - assertEquals(is1.available(), 0); - assertEquals(is2.available(), 0); + assertEquals(0, is1.available()); + assertEquals(0, is2.available()); } /* @@ -200,7 +200,7 @@ public class TransferTo { long reported = in.transferTo(out); int count = inBytes.length - posIn; - assertEquals(reported, count, + assertEquals(count, reported, format("reported %d bytes but should report %d", reported, count)); byte[] outBytes = recorder.get().get(); diff --git a/test/jdk/java/io/Writer/NullWriter.java b/test/jdk/java/io/Writer/NullWriter.java index d632947f129..e37c8bd54dd 100644 --- a/test/jdk/java/io/Writer/NullWriter.java +++ b/test/jdk/java/io/Writer/NullWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -21,144 +21,146 @@ * questions. */ -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - import java.io.IOException; import java.io.Writer; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertSame; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @bug 8196298 - * @run testng NullWriter + * @run junit NullWriter * @summary Check for expected behavior of Writer.nullWriter(). */ public class NullWriter { private static Writer openWriter; private static Writer closedWriter; - @BeforeClass + @BeforeAll public static void setup() throws IOException { openWriter = Writer.nullWriter(); closedWriter = Writer.nullWriter(); closedWriter.close(); } - @AfterClass + @AfterAll public static void closeStream() throws IOException { openWriter.close(); } @Test - public static void testOpen() { + public void testOpen() { assertNotNull(openWriter, "Writer.nullWriter() returned null"); } @Test - public static void testAppendChar() throws IOException { + public void testAppendChar() throws IOException { assertSame(openWriter, openWriter.append('x')); } @Test - public static void testAppendCharSequence() throws IOException { + public void testAppendCharSequence() throws IOException { CharSequence cs = "abc"; assertSame(openWriter, openWriter.append(cs)); } @Test - public static void testAppendCharSequenceNull() throws IOException { + public void testAppendCharSequenceNull() throws IOException { assertSame(openWriter, openWriter.append(null)); } @Test - public static void testAppendCharSequenceII() throws IOException { + public void testAppendCharSequenceII() throws IOException { CharSequence cs = "abc"; assertSame(openWriter, openWriter.append(cs, 0, 1)); } @Test - public static void testAppendCharSequenceIINull() throws IOException { + public void testAppendCharSequenceIINull() throws IOException { assertSame(openWriter, openWriter.append(null, 2, 1)); } @Test - public static void testFlush() throws IOException { + public void testFlush() throws IOException { openWriter.flush(); } @Test - public static void testWrite() throws IOException { + public void testWrite() throws IOException { openWriter.write(62832); } @Test - public static void testWriteString() throws IOException { + public void testWriteString() throws IOException { openWriter.write(""); } @Test - public static void testWriteStringII() throws IOException { + public void testWriteStringII() throws IOException { openWriter.write("", 0, 0); } @Test - public static void testWriteBII() throws IOException, Exception { + public void testWriteBII() throws IOException, Exception { openWriter.write(new char[]{(char) 6}, 0, 1); } - @Test(expectedExceptions = IOException.class) - public static void testAppendCharClosed() throws IOException { - closedWriter.append('x'); + @Test + public void testAppendCharClosed() throws IOException { + assertThrows(IOException.class, () -> closedWriter.append('x')); } - @Test(expectedExceptions = IOException.class) - public static void testAppendCharSequenceClosed() throws IOException { + @Test + public void testAppendCharSequenceClosed() throws IOException { CharSequence cs = "abc"; - closedWriter.append(cs); + assertThrows(IOException.class, () -> closedWriter.append(cs)); } - @Test(expectedExceptions = IOException.class) - public static void testAppendCharSequenceNullClosed() throws IOException { - closedWriter.append(null); + @Test + public void testAppendCharSequenceNullClosed() throws IOException { + assertThrows(IOException.class, () -> closedWriter.append(null)); } - @Test(expectedExceptions = IOException.class) - public static void testAppendCharSequenceIIClosed() throws IOException { + @Test + public void testAppendCharSequenceIIClosed() throws IOException { CharSequence cs = "abc"; - closedWriter.append(cs, 0, 1); + assertThrows(IOException.class, () -> closedWriter.append(cs, 0, 1)); } - @Test(expectedExceptions = IOException.class) - public static void testAppendCharSequenceIINullClosed() throws IOException { - closedWriter.append(null, 2, 1); + @Test + public void testAppendCharSequenceIINullClosed() throws IOException { + assertThrows(IOException.class, () -> closedWriter.append(null, 2, 1)); } - @Test(expectedExceptions = IOException.class) - public static void testFlushClosed() throws IOException { - closedWriter.flush(); + @Test + public void testFlushClosed() throws IOException { + assertThrows(IOException.class, () -> closedWriter.flush()); } - @Test(expectedExceptions = IOException.class) - public static void testWriteClosed() throws IOException { - closedWriter.write(62832); + @Test + public void testWriteClosed() throws IOException { + assertThrows(IOException.class, () -> closedWriter.write(62832)); } - @Test(expectedExceptions = IOException.class) - public static void testWriteStringClosed() throws IOException { - closedWriter.write(""); + @Test + public void testWriteStringClosed() throws IOException { + assertThrows(IOException.class, () -> closedWriter.write("")); } - @Test(expectedExceptions = IOException.class) - public static void testWriteStringIIClosed() throws IOException { - closedWriter.write("", 0, 0); + @Test + public void testWriteStringIIClosed() throws IOException { + assertThrows(IOException.class, () -> closedWriter.write("", 0, 0)); } - @Test(expectedExceptions = IOException.class) - public static void testWriteBIIClosed() throws IOException { - closedWriter.write(new char[]{(char) 6}, 0, 1); + @Test + public void testWriteBIIClosed() throws IOException { + assertThrows(IOException.class, + () -> closedWriter.write(new char[]{(char) 6}, 0, 1)); } } diff --git a/test/jdk/java/lang/Character/Latin1CaseConversion.java b/test/jdk/java/lang/Character/Latin1CaseConversion.java index a176bd4b002..436193c5f16 100644 --- a/test/jdk/java/lang/Character/Latin1CaseConversion.java +++ b/test/jdk/java/lang/Character/Latin1CaseConversion.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -21,17 +21,16 @@ * questions. */ -import org.testng.annotations.Test; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; /** * @test * @bug 8302877 * @summary Provides exhaustive verification of Character.toUpperCase and Character.toLowerCase * for all code points in the latin1 range 0-255. - * @run testng Latin1CaseConversion + * @run junit Latin1CaseConversion */ public class Latin1CaseConversion { @@ -44,41 +43,41 @@ public class Latin1CaseConversion { if (c < 0x41) { // Before A assertUnchanged(upper, lower, c); } else if (c <= 0x5A) { // A-Z - assertEquals(upper, c); - assertEquals(lower, c + 32); + assertEquals(c, upper); + assertEquals(c + 32, lower); } else if (c < 0x61) { // Between Z and a assertUnchanged(upper, lower, c); } else if (c <= 0x7A) { // a-z - assertEquals(upper, c - 32); - assertEquals(lower, c); + assertEquals(c - 32, upper); + assertEquals(c, lower); } else if (c < 0xB5) { // Between z and Micro Sign assertUnchanged(upper, lower, c); } else if (c == 0xB5) { // Special case for Micro Sign - assertEquals(upper, 0x39C); - assertEquals(lower, c); + assertEquals(0x39C, upper); + assertEquals(c, lower); } else if (c < 0xC0) { // Between my and A-grave assertUnchanged(upper, lower, c); } else if (c < 0xD7) { // A-grave - O with Diaeresis - assertEquals(upper, c); - assertEquals(lower, c + 32); + assertEquals(c, upper); + assertEquals(c + 32, lower); } else if (c == 0xD7) { // Multiplication assertUnchanged(upper, lower, c); } else if (c <= 0xDE) { // O with slash - Thorn - assertEquals(upper, c); - assertEquals(lower, c + 32); + assertEquals(c, upper); + assertEquals(c + 32, lower); } else if (c == 0xDF) { // Sharp s assertUnchanged(upper, lower, c); } else if (c < 0xF7) { // a-grave - divsion - assertEquals(upper, c - 32); - assertEquals(lower, c); + assertEquals(c - 32, upper); + assertEquals(c, lower); } else if (c == 0xF7) { // Division assertUnchanged(upper, lower, c); } else if (c < 0xFF) { // o with slash - thorn - assertEquals(upper, c - 32); - assertEquals(lower, c); + assertEquals(c - 32, upper); + assertEquals(c, lower); } else if (c == 0XFF) { // Special case for y with Diaeresis - assertEquals(upper, 0x178); - assertEquals(lower, c); + assertEquals(0x178, upper); + assertEquals(c, lower); } else { fail("Uncovered code point: " + Integer.toHexString(c)); } @@ -86,7 +85,7 @@ public class Latin1CaseConversion { } private static void assertUnchanged(int upper, int lower, int c) { - assertEquals(upper, c); - assertEquals(lower, c); + assertEquals(c, upper); + assertEquals(c, lower); } } diff --git a/test/jdk/java/lang/Character/UnicodeBlock/NumberEntities.java b/test/jdk/java/lang/Character/UnicodeBlock/NumberEntities.java index 30382c3cfe3..86b9fe6ea69 100644 --- a/test/jdk/java/lang/Character/UnicodeBlock/NumberEntities.java +++ b/test/jdk/java/lang/Character/UnicodeBlock/NumberEntities.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -28,17 +28,17 @@ * of Character.UnicodeBlock constants. Also checks the size of * Character.UnicodeScript's "aliases" map. * @modules java.base/java.lang:open - * @run testng NumberEntities + * @run junit NumberEntities */ -import static org.testng.Assert.assertEquals; -import org.testng.annotations.Test; - import java.lang.reflect.Field; import java.util.Map; -@Test +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class NumberEntities { + @Test public void test_UnicodeBlock_NumberEntities() throws Throwable { // The number of entries in Character.UnicodeBlock.map. // See src/java.base/share/classes/java/lang/Character.java @@ -46,13 +46,14 @@ public class NumberEntities { Field m = Character.UnicodeBlock.class.getDeclaredField("map"); n.setAccessible(true); m.setAccessible(true); - assertEquals(((Map)m.get(null)).size(), n.getInt(null)); + assertEquals(n.getInt(null), ((Map)m.get(null)).size()); } + @Test public void test_UnicodeScript_aliases() throws Throwable { // The number of entries in Character.UnicodeScript.aliases. // See src/java.base/share/classes/java/lang/Character.java Field aliases = Character.UnicodeScript.class.getDeclaredField("aliases"); aliases.setAccessible(true); - assertEquals(((Map)aliases.get(null)).size(), Character.UnicodeScript.UNKNOWN.ordinal() + 1); + assertEquals(Character.UnicodeScript.UNKNOWN.ordinal() + 1, ((Map)aliases.get(null)).size()); } } diff --git a/test/jdk/java/lang/Math/HyperbolicTests.java b/test/jdk/java/lang/Math/HyperbolicTests.java index ac5526c1953..f45f0d25a48 100644 --- a/test/jdk/java/lang/Math/HyperbolicTests.java +++ b/test/jdk/java/lang/Math/HyperbolicTests.java @@ -2233,7 +2233,7 @@ public class HyperbolicTests { } /** - * Test accuracy of {Math, StrictMath}.tanh using quad precision + * Test accuracy of {Math, StrictMath}.atanh using quad precision * tanh implementation as the reference. There are additional tests. * The specified accuracy is 2.5 ulps. * diff --git a/test/jdk/java/lang/ProcessBuilder/Basic.java b/test/jdk/java/lang/ProcessBuilder/Basic.java index dd97de8fad8..70d6101c1a5 100644 --- a/test/jdk/java/lang/ProcessBuilder/Basic.java +++ b/test/jdk/java/lang/ProcessBuilder/Basic.java @@ -22,32 +22,42 @@ */ /* - * @test + * @test id=POSIX_SPAWN * @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 4986689 * 5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313 * 6464154 6523983 6206031 4960438 6631352 6631966 6850957 6850958 * 4947220 7018606 7034570 4244896 5049299 8003488 8054494 8058464 * 8067796 8224905 8263729 8265173 8272600 8231297 8282219 8285517 - * 8352533 8368192 + * 8352533 8368192 8377907 * @key intermittent * @summary Basic tests for Process and Environment Variable code * @modules java.base/java.lang:open * java.base/java.io:open * @requires vm.flagless * @library /test/lib - * @run main/othervm/native/timeout=360 Basic - * @run main/othervm/native/timeout=360 -Djdk.lang.Process.launchMechanism=fork Basic + * @run main/othervm/native/timeout=360 -Djdk.lang.Process.launchMechanism=posix_spawn Basic * @author Martin Buchholz */ /* - * @test + * @test id=FORK + * @key intermittent + * @summary Basic tests for Process and Environment Variable code + * @modules java.base/java.lang:open + * java.base/java.io:open + * @requires vm.flagless + * @library /test/lib + * @run main/othervm/native/timeout=360 -Djdk.lang.Process.launchMechanism=fork Basic + */ + +/* + * @test id=VFORK * @modules java.base/java.lang:open * java.base/java.io:open * java.base/jdk.internal.misc * @requires (os.family == "linux") * @library /test/lib - * @run main/othervm/timeout=300 -Djdk.lang.Process.launchMechanism=posix_spawn Basic + * @run main/othervm/timeout=300 -Djdk.lang.Process.launchMechanism=vfork Basic */ import java.lang.ProcessBuilder.Redirect; @@ -1229,6 +1239,20 @@ public class Basic { equal(r.out(), "standard output"); equal(r.err(), "standard error"); } + + //---------------------------------------------------------------- + // Default: should go to pipes (use a fresh ProcessBuilder) + //---------------------------------------------------------------- + { + ProcessBuilder pb2 = new ProcessBuilder(childArgs); + Process p = pb2.start(); + new PrintStream(p.getOutputStream()).print("standard input"); + p.getOutputStream().close(); + ProcessResults r = run(p); + equal(r.exitValue(), 0); + equal(r.out, "standard output"); + equal(r.err, "standard error"); + } } static void checkProcessPid() { @@ -1267,6 +1291,8 @@ public class Basic { if (UnicodeOS.is()) System.out.println("This appears to be a Unicode-based OS."); + System.out.println("Using:" + System.getProperty("jdk.lang.Process.launchMechanism")); + try { testIORedirection(); } catch (Throwable t) { unexpected(t); } diff --git a/test/jdk/java/lang/ProcessBuilder/ConcNativeForkTest/ConcNativeForkTest.java b/test/jdk/java/lang/ProcessBuilder/ConcNativeForkTest/ConcNativeForkTest.java new file mode 100644 index 00000000000..f02e4991302 --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/ConcNativeForkTest/ConcNativeForkTest.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corp. + * 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 id=POSIX_SPAWN + * @bug 8377907 + * @summary Test that demonstrates the hanging-parent-on-native-concurrent-forks problem + * @requires os.family != "windows" + * @requires vm.flagless + * @library /test/lib + * @run main/othervm/manual -Djdk.lang.Process.launchMechanism=POSIX_SPAWN ConcNativeForkTest + */ + +/* + * @test id=FORK + * @bug 8377907 + * @summary Test that demonstrates the hanging-parent-on-native-concurrent-forks problem + * @requires os.family != "windows" + * @requires vm.flagless + * @library /test/lib + * @run main/othervm/manual -Djdk.lang.Process.launchMechanism=FORK ConcNativeForkTest + */ + +/* + * @test id=VFORK + * @bug 8377907 + * @summary Test that demonstrates the hanging-parent-on-native-concurrent-forks problem + * @requires os.family == "linux" + * @requires vm.flagless + * @library /test/lib + * @run main/othervm/manual -Djdk.lang.Process.launchMechanism=VFORK ConcNativeForkTest + */ + +public class ConcNativeForkTest { + + // How this works: + // - We start a child process via ProcessBuilder. Does not matter what, we just call "/bin/true". + // - Concurrently, we continuously (up to a limit) fork natively; these forks will all exec "sleep 30". + // - If the natively forked child process forks off at the right (wrong) moment, it will catch the open pipe from + // the "/bin/true" child process, and forcing the parent process (this test) to wait in ProcessBuilder.start() + // (inside forkAndExec()) until the natively forked child releases the pipe file descriptors it inherited. + + // Notes: + // + // Obviously, this is racy and depends on scheduler timings of the underlying OS. The test succeeding is + // no proof the bug does not exist (see PipesCloseOnExecTest as a complimentary test that is more reliable, but + // only works on Linux). + // That said, in tests it reliably reproduces the bug on Linux x64 and MacOS Arm. + // + // This test is not well suited for automatic test execution, since the test essentially + // fork-bombs itself, and that may run into issues in containerized CI/CD environments. + + native static boolean prepareNativeForkerThread(int numForks); + native static void releaseNativeForkerThread(); + native static void stopNativeForkerThread(); + + private static final int numIterations = 20; + + public static void main(String[] args) throws Exception { + + System.out.println("jdk.lang.Process.launchMechanism=" + + System.getProperty("jdk.lang.Process.launchMechanism")); + + System.loadLibrary("ConcNativeFork"); + + // A very simple program returning immediately (/bin/true) + ProcessBuilder pb = new ProcessBuilder("true").inheritIO(); + final int numJavaProcesses = 10; + final int numNativeProcesses = 250; + Process[] processes = new Process[numJavaProcesses]; + + for (int iteration = 0; iteration < numIterations; iteration ++) { + + if (!prepareNativeForkerThread(numNativeProcesses)) { + throw new RuntimeException("Failed to start native forker thread (see stdout)"); + } + + long[] durations = new long[numJavaProcesses]; + + releaseNativeForkerThread(); + + for (int np = 0; np < numJavaProcesses; np ++) { + long t1 = System.currentTimeMillis(); + try (Process p = pb.start()) { + durations[np] = System.currentTimeMillis() - t1; + processes[np] = p; + } + } + + stopNativeForkerThread(); + + long longestDuration = 0; + for (int np = 0; np < numJavaProcesses; np ++) { + processes[np].waitFor(); + System.out.printf("Duration: %dms%n", durations[np]); + longestDuration = Math.max(durations[np], longestDuration); + } + + System.out.printf("Longest startup time: %dms%n", longestDuration); + + if (longestDuration >= 30000) { + throw new RuntimeException("Looks like we blocked on native fork"); + } + } + + } + +} diff --git a/test/jdk/java/lang/ProcessBuilder/ConcNativeForkTest/libConcNativeFork.c b/test/jdk/java/lang/ProcessBuilder/ConcNativeForkTest/libConcNativeFork.c new file mode 100644 index 00000000000..59729308558 --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/ConcNativeForkTest/libConcNativeFork.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corp. + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "testlib_thread_barriers.h" + +static void trc(const char* fmt, ...) { + char buf [1024]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + printf("(pid: %d): %s\n", (int)getpid(), buf); + fflush(stdout); +} + +static pthread_t tid_forker; + +static pthread_barrier_t start_barrier; +static atomic_bool stop_now = false; + +static void* forkerLoop(void* info) { + + const int numForks = (int)(intptr_t)info; + pid_t* pids = calloc(numForks, sizeof(pid_t)); + + trc("Forker: Waiting for Go."); + + pthread_barrier_wait(&start_barrier); + + for (int i = 0; i < numForks; i++) { + const pid_t pid = fork(); + if (pid == 0) { + /* Exec sleep. Properly opened file descriptors in parents (tagged CLOEXEC) should be released now. + * Note that we use bash to not have to deal with path resolution. For our case, it does not matter if + * sleep is a builtin or not. */ + char* env[] = { "PATH=/usr/bin:/bin", NULL }; + char* argv[] = { "sh", "-c", "sleep 30", NULL }; + execve("/bin/sh", argv, env); + trc("Native child: sleep exec failed? %d", errno); + /* The simplest way to handle this is to just wait here; this *will* cause the test to fail. */ + sleep(120); + trc("Native child: exiting"); + exit(0); + } else { + pids[i] = pid; + sched_yield(); + } + } + + trc("Forker: All native child processes started."); + + /* Wait for test to signal end */ + while (!atomic_load(&stop_now)) { + sleep(1); + } + + trc("Forker: Cleaning up."); + + /* Reap children */ + for (int i = 0; i < numForks; i ++) { + if (pids[i] != 0) { + kill(pids[i], SIGKILL); /* if still running */ + waitpid(pids[i], NULL, 0); + } + } + + trc("Forker: Done."); + + return NULL; +} + +JNIEXPORT jboolean JNICALL +Java_ConcNativeForkTest_prepareNativeForkerThread(JNIEnv* env, jclass cls, jint numForks) +{ + pthread_attr_t attr; + int rc = 0; + + const int cap = 1000; + const int numForksCapped = numForks > cap ? cap : numForks; + if (numForks > numForksCapped) { + trc("Main: Capping max. number of forks at %d", numForksCapped); /* don't forkbomb me */ + } + + if (pthread_barrier_init(&start_barrier, NULL, 2) != 0) { + trc("Main: pthread_barrier_init failed (%d)", errno); + return false; + } + + pthread_attr_init(&attr); + if (pthread_create(&tid_forker, &attr, forkerLoop, (void*)(intptr_t)numForksCapped) != 0) { + trc("Main: pthread_create failed (%d)", errno); + return JNI_FALSE; + } + + trc("Main: Prepared native forker thread"); + + return JNI_TRUE; +} + +JNIEXPORT void JNICALL +Java_ConcNativeForkTest_releaseNativeForkerThread(JNIEnv* env, jclass cls) +{ + pthread_barrier_wait(&start_barrier); + trc("Main: signaled GO"); +} + +JNIEXPORT void JNICALL +Java_ConcNativeForkTest_stopNativeForkerThread(JNIEnv* env, jclass cls) +{ + atomic_store(&stop_now, true); + pthread_join(tid_forker, NULL); + pthread_barrier_destroy(&start_barrier); +} diff --git a/test/jdk/java/lang/ProcessBuilder/FDLeakTest/FDLeakTest.java b/test/jdk/java/lang/ProcessBuilder/FDLeakTest/FDLeakTest.java index 146c2be563f..aee85e2335f 100644 --- a/test/jdk/java/lang/ProcessBuilder/FDLeakTest/FDLeakTest.java +++ b/test/jdk/java/lang/ProcessBuilder/FDLeakTest/FDLeakTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -24,7 +24,7 @@ /** * @test id=posix_spawn - * @summary Check that we don't leak FDs + * @summary Check that we don't leak FDs to child processes * @requires os.family != "windows" * @library /test/lib * @run main/othervm/native -Djdk.lang.Process.launchMechanism=posix_spawn -agentlib:FDLeaker FDLeakTest @@ -32,7 +32,7 @@ /** * @test id=fork - * @summary Check that we don't leak FDs + * @summary Check that we don't leak FDs to child processes * @requires os.family != "windows" * @library /test/lib * @run main/othervm/native -Djdk.lang.Process.launchMechanism=fork -agentlib:FDLeaker FDLeakTest @@ -40,7 +40,7 @@ /** * @test id=vfork - * @summary Check that we don't leak FDs + * @summary Check that we don't leak FDs to child processes * @requires os.family == "linux" * @library /test/lib * @run main/othervm/native -Djdk.lang.Process.launchMechanism=vfork -agentlib:FDLeaker FDLeakTest diff --git a/test/jdk/java/lang/ProcessBuilder/InheritIOTest.java b/test/jdk/java/lang/ProcessBuilder/InheritIOTest.java index a372eeefbf8..b3f52663a59 100644 --- a/test/jdk/java/lang/ProcessBuilder/InheritIOTest.java +++ b/test/jdk/java/lang/ProcessBuilder/InheritIOTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -23,21 +23,22 @@ /* * @test - * @bug 8023130 8166026 * @summary Unit test for java.lang.ProcessBuilder inheritance of standard output and standard error streams + * @bug 8023130 8166026 * @requires vm.flagless * @library /test/lib * @build jdk.test.lib.process.* - * @run testng InheritIOTest + * @run junit InheritIOTest */ import java.util.List; import static java.lang.ProcessBuilder.Redirect.INHERIT; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; public class InheritIOTest { @@ -45,25 +46,25 @@ public class InheritIOTest { private static final String EXPECTED_RESULT_STDOUT = "message"; private static final String EXPECTED_RESULT_STDERR = EXIT_VALUE_TEMPLATE.formatted(0); - @DataProvider - public Object[][] testCases() { - return new Object[][]{ - new Object[] { List.of("InheritIOTest$TestInheritIO", "printf", EXPECTED_RESULT_STDOUT) }, - new Object[] { List.of("InheritIOTest$TestRedirectInherit", "printf", EXPECTED_RESULT_STDOUT) } - }; + public static List testCases() { + return List.of( + Arguments.of(List.of("InheritIOTest$TestInheritIO", "printf", EXPECTED_RESULT_STDOUT)), + Arguments.of(List.of("InheritIOTest$TestRedirectInherit", "printf", EXPECTED_RESULT_STDOUT)) + ); } - @Test(dataProvider = "testCases") + @ParameterizedTest + @MethodSource("testCases") public void testInheritWithoutRedirect(List arguments) throws Throwable { ProcessBuilder processBuilder = ProcessTools.createLimitedTestJavaProcessBuilder(arguments); OutputAnalyzer outputAnalyzer = ProcessTools.executeCommand(processBuilder); outputAnalyzer.shouldHaveExitValue(0); - assertEquals(outputAnalyzer.getStdout(), EXPECTED_RESULT_STDOUT); - assertEquals(outputAnalyzer.getStderr(), EXPECTED_RESULT_STDERR); + assertEquals(EXPECTED_RESULT_STDOUT, outputAnalyzer.getStdout()); + assertEquals(EXPECTED_RESULT_STDERR, outputAnalyzer.getStderr()); } public static class TestInheritIO { - public static void main(String args[]) throws Throwable { + public static void main(String[] args) throws Throwable { int err = new ProcessBuilder(args).inheritIO().start().waitFor(); System.err.printf(EXIT_VALUE_TEMPLATE, err); System.exit(err); @@ -71,7 +72,7 @@ public class InheritIOTest { } public static class TestRedirectInherit { - public static void main(String args[]) throws Throwable { + public static void main(String[] args) throws Throwable { int err = new ProcessBuilder(args) .redirectInput(INHERIT) .redirectOutput(INHERIT) @@ -81,5 +82,4 @@ public class InheritIOTest { System.exit(err); } } - } diff --git a/test/jdk/java/lang/ProcessBuilder/InvalidWorkDir.java b/test/jdk/java/lang/ProcessBuilder/InvalidWorkDir.java new file mode 100644 index 00000000000..310ecf03f97 --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/InvalidWorkDir.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2026, 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 id=FORK + * @bug 8379967 + * @summary Check that passing an invalid work dir yields a corresponding IOE text. + * @requires (os.family != "windows") + * @requires vm.flagless + * @library /test/lib + * @run main/othervm -Xmx64m -Djdk.lang.Process.launchMechanism=FORK InvalidWorkDir + */ + +/** + * @test id=POSIX_SPAWN + * @bug 8379967 + * @summary Check that passing an invalid work dir yields a corresponding IOE text. + * @requires (os.family != "windows") + * @requires vm.flagless + * @library /test/lib + * @run main/othervm -Xmx64m -Djdk.lang.Process.launchMechanism=FORK InvalidWorkDir + */ + +import jdk.test.lib.process.OutputAnalyzer; + +import java.io.File; +import java.io.IOException; + +public class InvalidWorkDir { + + public static void main(String[] args) { + ProcessBuilder bld = new ProcessBuilder("ls").directory(new File("./doesnotexist")); + try(Process p = bld.start()) { + throw new RuntimeException("IOE expected"); + } catch (IOException e) { + if (!e.getMessage().matches(".*Failed to access working directory.*No such file or directory.*")) { + throw new RuntimeException(String.format("got IOE but with different text (%s)", e.getMessage())); + } + } + } + +} diff --git a/test/jdk/java/lang/ProcessBuilder/JspawnhelperProtocol.java b/test/jdk/java/lang/ProcessBuilder/JspawnhelperProtocol.java index 0098bc531fd..59ce9450cb9 100644 --- a/test/jdk/java/lang/ProcessBuilder/JspawnhelperProtocol.java +++ b/test/jdk/java/lang/ProcessBuilder/JspawnhelperProtocol.java @@ -39,6 +39,7 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; public class JspawnhelperProtocol { @@ -55,7 +56,9 @@ public class JspawnhelperProtocol { System.out.println("Recursively executing 'JspawnhelperProtocol " + arg + "'"); Process p = null; try { - p = Runtime.getRuntime().exec(CMD); + // Route any stdout from the child process - be it jspawnhelper error messages or the output of "/bin/pwd" - + // through to the parent process. + p = new ProcessBuilder(CMD).inheritIO().start(); } catch (Exception e) { // Check that exception contains rich message on failure. e.printStackTrace(System.out); @@ -70,12 +73,6 @@ public class JspawnhelperProtocol { System.exit(ERROR + 1); } if (p.exitValue() == 0) { - String pwd = p.inputReader().readLine(); - String realPwd = Path.of("").toAbsolutePath().toString(); - if (!realPwd.equals(pwd)) { - System.out.println("Child process returned '" + pwd + "' (expected '" + realPwd + "')"); - System.exit(ERROR + 2); - } System.out.println(" Successfully executed '" + CMD[0] + "'"); System.exit(0); } else { @@ -89,7 +86,6 @@ public class JspawnhelperProtocol { pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Djdk.lang.Process.launchMechanism=posix_spawn", "JspawnhelperProtocol", "normalExec"); - pb.inheritIO(); Process p = pb.start(); if (!p.waitFor(TIMEOUT, TimeUnit.SECONDS)) { throw new Exception("Parent process timed out"); @@ -97,6 +93,10 @@ public class JspawnhelperProtocol { if (p.exitValue() != 0) { throw new Exception("Parent process exited with " + p.exitValue()); } + OutputAnalyzer output = new OutputAnalyzer(p); + output.shouldContain("Recursively executing 'JspawnhelperProtocol normalExec'"); + String realPwd = Path.of("").toAbsolutePath().toString(); + output.shouldContain(realPwd); } private static void simulateCrashInChild(int stage) throws Exception { @@ -144,7 +144,7 @@ public class JspawnhelperProtocol { try (BufferedReader br = p.inputReader()) { line = br.readLine(); while (line != null && !line.startsWith("posix_spawn:")) { - System.out.println(line); + System.out.println("parent stdout:" + line); line = br.readLine(); } } diff --git a/test/jdk/java/lang/ProcessBuilder/JspawnhelperWarnings.java b/test/jdk/java/lang/ProcessBuilder/JspawnhelperWarnings.java index d9896f16e00..fec0b4f3efd 100644 --- a/test/jdk/java/lang/ProcessBuilder/JspawnhelperWarnings.java +++ b/test/jdk/java/lang/ProcessBuilder/JspawnhelperWarnings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -22,11 +22,19 @@ */ /* - * @test - * @bug 8325567 8325621 + * @test id=badargs + * @bug 8325567 8325621 8379967 * @requires (os.family == "linux") | (os.family == "aix") | (os.family == "mac") * @library /test/lib - * @run driver JspawnhelperWarnings + * @run driver JspawnhelperWarnings badargs + */ + +/* + * @test id=badversion + * @bug 8325567 8325621 8379967 + * @requires (os.family == "linux") | (os.family == "aix") | (os.family == "mac") + * @library /test/lib + * @run driver JspawnhelperWarnings badversion */ import java.nio.file.Paths; @@ -36,6 +44,13 @@ import jdk.test.lib.process.ProcessTools; public class JspawnhelperWarnings { + // See childproc_errorcodes.h + static final int ESTEP_JSPAWN_ARG_ERROR = 1; + static final int ESTEP_JSPAWN_VERSION_ERROR = 2; + + // See exitCodeFromErrorCode() in childproc_errorcodes.c + static final int EXITCODE_OFFSET = 0x10; + private static void tryWithNArgs(int nArgs) throws Exception { System.out.println("Running jspawnhelper with " + nArgs + " args"); String[] args = new String[nArgs + 1]; @@ -43,9 +58,10 @@ public class JspawnhelperWarnings { args[0] = Paths.get(System.getProperty("java.home"), "lib", "jspawnhelper").toString(); Process p = ProcessTools.startProcess("jspawnhelper", new ProcessBuilder(args)); OutputAnalyzer oa = new OutputAnalyzer(p); - oa.shouldHaveExitValue(1); + oa.shouldHaveExitValue(EXITCODE_OFFSET + ESTEP_JSPAWN_ARG_ERROR); + oa.shouldContain("jspawnhelper fail: (1-0-0)"); oa.shouldContain("This command is not for general use"); - if (nArgs != 2) { + if (nArgs != 1) { oa.shouldContain("Incorrect number of arguments"); } else { oa.shouldContain("Incorrect Java version"); @@ -53,22 +69,33 @@ public class JspawnhelperWarnings { } private static void testVersion() throws Exception { - String[] args = new String[3]; + String[] args = new String[2]; args[0] = Paths.get(System.getProperty("java.home"), "lib", "jspawnhelper").toString(); args[1] = "wrongVersion"; - args[2] = "1:1:1"; Process p = ProcessTools.startProcess("jspawnhelper", new ProcessBuilder(args)); OutputAnalyzer oa = new OutputAnalyzer(p); - oa.shouldHaveExitValue(1); + oa.shouldHaveExitValue(EXITCODE_OFFSET + ESTEP_JSPAWN_VERSION_ERROR); + oa.shouldContain("jspawnhelper fail: (2-0-0)"); oa.shouldContain("This command is not for general use"); oa.shouldContain("Incorrect Java version: wrongVersion"); } public static void main(String[] args) throws Exception { - for (int nArgs = 0; nArgs < 10; nArgs++) { - tryWithNArgs(nArgs); + if (args.length != 1) { + throw new RuntimeException("test argument error"); + } + switch (args[0]) { + case "badargs" -> { + for (int nArgs = 0; nArgs < 10; nArgs++) { + if (nArgs != 1) { + tryWithNArgs(nArgs); + } + } + } + case "badversion" -> { + testVersion(); + } + default -> throw new RuntimeException("test argument error"); } - - testVersion(); } } diff --git a/test/jdk/java/lang/ProcessBuilder/LinuxFDInfo.java b/test/jdk/java/lang/ProcessBuilder/LinuxFDInfo.java new file mode 100644 index 00000000000..6e0b08c5879 --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/LinuxFDInfo.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2026, 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. + */ + +/** + * Accessor for Native calls to get information about File Descriptors. + * If it's a pipe and its Inode number. + */ +public class LinuxFDInfo { + + static { + System.loadLibrary("LinuxFDInfo"); + } + + // Maximum file descriptor to probe for being a pipe, + private final static int MAX_FD = 100; + + // A simple main to print the pipes in this process and their Inode value + public static void main() { + for (int fd = 0; fd < MAX_FD; fd++) { + long inode = getPipeInodeNum(fd); + if (inode != 0) { + System.out.printf("fd: %d, inode: 0x%08x\n", fd, inode); + } + } + } + + // Parse the output from main into a long array of the fd, and Inode. + public static FdAndInode parseFdAndInode(String s) { + String[] args = s.split(","); + return new FdAndInode(Integer.parseUnsignedInt(args[0].split(":")[1].trim()), + Long.parseUnsignedLong(args[1].split(":")[1].trim().substring(2), 16)); + } + + /** + * Return the inode number for the FD, if it is a pipe. + * @param fd file descriptor + * @return the Inode number. + */ + public static native long getPipeInodeNum(int fd); + + public record FdAndInode(int fd, long inode) {} +} diff --git a/test/jdk/java/lang/ProcessBuilder/NonPipelineLeaksFD.java b/test/jdk/java/lang/ProcessBuilder/NonPipelineLeaksFD.java new file mode 100644 index 00000000000..be2c4af3bbe --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/NonPipelineLeaksFD.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corp. + * 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.io.*; + +/* + * @test id=FORK + * @summary Check that we don't accumulate leaked FDs in the parent process + * @requires os.family == "linux" + * @library /test/lib + * @run main/othervm -Djdk.lang.Process.launchMechanism=fork NonPipelineLeaksFD + */ + +/* + * @test id=VFORK + * @summary Check that we don't accumulate leaked FDs in the parent process + * @requires os.family == "linux" + * @library /test/lib + * @run main/othervm -Djdk.lang.Process.launchMechanism=vfork NonPipelineLeaksFD + */ + +/* + * @test id=POSIX_SPAWN + * @summary Check that we don't accumulate leaked FDs in the parent process + * @requires os.family == "linux" + * @library /test/lib + * @run main/othervm -Djdk.lang.Process.launchMechanism=posix_spawn NonPipelineLeaksFD + */ + +public class NonPipelineLeaksFD { + + final static int repeatCount = 50; + + // Similar to PilelineLeaksFD, but where PilelineLeaksFD checks that the parent process + // does not leak file descriptors when invoking a pipeline, here we check that we don't + // leak FDs when executing simple (non-pipelined) programs but we test a wider span of + // redirection modes in both successful and failing variants. + + // How this works: + // + // We execute a mix of failing and succeeding child process starts with various + // flavors of IO redirections many times; we observe the open file descriptors + // before and afterwards. Test fails if we have significantly more file descriptors + // open afterwards than before. + + static int countNumberOfOpenFileDescriptors() { + return new File("/proc/self/fd").list().length; + } + + static void printOpenFileDescriptors() { + long mypid = ProcessHandle.current().pid(); + try(Process p = new ProcessBuilder("lsof", "-p", Long.toString(mypid)) + .inheritIO().start()) { + p.waitFor(); + } catch (InterruptedException | IOException ignored) { + // Quietly swallow; it was just an attempt. + } + } + + static String readFirstLineOf(File f) { + String result; + try (BufferedReader b = new BufferedReader(new FileReader(f))){ + result = b.readLine(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return result; + } + + static void runThisExpectSuccess(ProcessBuilder bld) { + try(Process p = bld.start()) { + p.waitFor(); + if (p.exitValue() != 0) { + throw new RuntimeException("Unexpected exitcode"); + } + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } + + static void runThisExpectError(ProcessBuilder bld) { + boolean failed = false; + try(Process p = bld.start()) { + p.waitFor(); + } catch (IOException | InterruptedException e) { + failed = true; + } + if (!failed) { + throw new RuntimeException("Expected Error"); + } + } + + static void runPosWithPipes() { + ProcessBuilder bld = new ProcessBuilder("sh", "-c", "echo hallo"); + runThisExpectSuccess(bld); + } + + static void runPosWithInheritIO() { + ProcessBuilder bld = new ProcessBuilder("sh", "-c", "echo hallo").inheritIO(); + runThisExpectSuccess(bld); + } + + static void runPosWithRedirectToFile() { + File fo = new File("test.out"); + ProcessBuilder bld = new ProcessBuilder("sh", "-c", "echo hallo"); + bld.redirectOutput(ProcessBuilder.Redirect.to(fo)); + runThisExpectSuccess(bld); + if (!readFirstLineOf(fo).equals("hallo")) { + throw new RuntimeException("mismatch"); + } + } + + static void runNegWithPipes() { + ProcessBuilder bld = new ProcessBuilder("doesnotexist"); + runThisExpectError(bld); + } + + static void runNegWithInheritIO() { + ProcessBuilder bld = new ProcessBuilder("doesnotexist").inheritIO(); + runThisExpectError(bld); + } + + static void runNegWithRedirectToFile() { + File fo = new File("test.out"); + ProcessBuilder bld = new ProcessBuilder("doesnotexist"); + bld.redirectOutput(ProcessBuilder.Redirect.to(fo)); + runThisExpectError(bld); + } + + static void doTestNTimesAndCountFDs(Runnable runnable, String name) { + System.out.println(name); + int c1 = countNumberOfOpenFileDescriptors(); + for (int i = 0; i < repeatCount; i++) { + runnable.run(); + } + int c2 = countNumberOfOpenFileDescriptors(); + System.out.printf("%d->%d", c1, c2); + } + + public static void main(String[] args) throws Exception { + System.out.println("jdk.lang.Process.launchMechanism=" + + System.getProperty("jdk.lang.Process.launchMechanism")); + int c1 = countNumberOfOpenFileDescriptors(); + doTestNTimesAndCountFDs(NonPipelineLeaksFD::runPosWithPipes, "runPosWithPipes"); + doTestNTimesAndCountFDs(NonPipelineLeaksFD::runPosWithInheritIO, "runPosWithInheritIO"); + doTestNTimesAndCountFDs(NonPipelineLeaksFD::runPosWithRedirectToFile, "runPosWithRedirectToFile"); + doTestNTimesAndCountFDs(NonPipelineLeaksFD::runNegWithPipes, "runNegWithPipes"); + doTestNTimesAndCountFDs(NonPipelineLeaksFD::runNegWithInheritIO, "runNegWithInheritIO"); + doTestNTimesAndCountFDs(NonPipelineLeaksFD::runNegWithRedirectToFile, "runNegWithRedirectToFile"); + int c2 = countNumberOfOpenFileDescriptors(); + + System.out.printf("All tests: %d->%d", c1, c2); + printOpenFileDescriptors(); + + final int fudge = 10; + if (c2 > (c1 + fudge)) { + throw new RuntimeException( + String.format("Leak suspected (%d->%d) - see lsof output", c1, c2)); + } + } +} diff --git a/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java b/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java index 92cca736503..d1213a5ee11 100644 --- a/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java +++ b/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java @@ -21,46 +21,50 @@ * questions. */ -import org.junit.jupiter.api.condition.EnabledIf; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import static org.junit.jupiter.api.Assertions.*; -import jdk.test.lib.Utils; import java.io.BufferedReader; import java.io.IOException; import java.io.Writer; -import java.lang.ProcessHandle; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.concurrent.TimeUnit; -import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; -import java.util.Optional; import java.util.Set; -import java.util.StringJoiner; import java.util.stream.Collectors; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + /* - * @test + * @test id=FORK * @bug 8289643 8291760 8291986 - * @requires os.family == "mac" | (os.family == "linux" & !vm.musl) + * @requires os.family == "mac" | os.family == "linux" * @summary File descriptor leak detection with ProcessBuilder.startPipeline * @library /test/lib - * @run junit/othervm/timeout=240 PipelineLeaksFD + * @run junit/othervm/timeout=240 -Djdk.lang.Process.launchMechanism=FORK PipelineLeaksFD + */ + +/* + * @test id=POSIX_SPAWN + * @bug 8289643 8291760 8291986 8379182 + * @requires os.family == "mac" | os.family == "linux" + * @summary File descriptor leak detection with ProcessBuilder.startPipeline + * @library /test/lib + * @run junit/othervm/native/timeout=240 -Djdk.lang.Process.launchMechanism=POSIX_SPAWN PipelineLeaksFD */ public class PipelineLeaksFD { - private static final String OS_NAME = System.getProperty("os.name", "Unknown"); + private static final String TEST_JDK = System.getProperty("test.jdk"); + + private static final String JAVA_LIBRARY_PATH = System.getProperty("java.library.path"); private static final long MY_PID = ProcessHandle.current().pid(); - private static final boolean LSOF_AVAILABLE = checkForLSOF(); + // Maximum file descriptor to probe for being a pipe, + private static final int MAX_FD = 100; // Test cases for pipelines with a number of pipeline sequences public static Object[][] builders() { @@ -76,37 +80,17 @@ public class PipelineLeaksFD { }; } - // True if lsof is runnable and collect pipe usage. - private static boolean lsofAvailable() { - return LSOF_AVAILABLE; - } - - // Check if lsof is available - private static boolean checkForLSOF() { - try { - lsofForPids(MY_PID); - return true; - } catch (IOException ioe) { - System.err.println("Skipping: " + ioe); - return false; - } - } - - @EnabledIf("lsofAvailable") @ParameterizedTest @MethodSource("builders") void checkForLeaks(List builders) throws IOException { - List lsofLines = lsofForPids(MY_PID); - Set pipesBefore = pipesFromLSOF(lsofLines, MY_PID); - if (pipesBefore.size() < 3) { - // Dump all lsof output to aid debugging - System.out.println("Output from lsof"); - lsofLines.forEach(System.err::println); - System.err.println(pipesBefore); - fail("There should be at least 3 pipes before, (0, 1, 2)"); + System.out.println("Using:" + System.getProperty("jdk.lang.Process.launchMechanism")); + Set beforePipes = pipesFromSelf(); + if (beforePipes.size() < 3) { + fail("There should be at least 3 pipes before, (0, 1, 2): is " + + beforePipes.size()); } - printPipes(pipesBefore, "Before start"); + printPipes(beforePipes, "Before start"); List processes = ProcessBuilder.startPipeline(builders); @@ -133,18 +117,14 @@ public class PipelineLeaksFD { processes.forEach(PipelineLeaksFD::waitForQuiet); - lsofLines = lsofForPids(MY_PID); - Set pipesAfter = pipesFromLSOF(lsofLines, MY_PID); - if (!pipesBefore.equals(pipesAfter)) { - Set missing = new HashSet<>(pipesBefore); - missing.removeAll(pipesAfter); - printPipes(missing, "Missing from pipesAfter"); - Set extra = new HashSet<>(pipesAfter); - extra.removeAll(pipesBefore); - printPipes(extra, "Extra pipes in pipesAfter"); - // Dump all lsof output to aid debugging - System.out.println("\nOutput from lsof"); - lsofLines.forEach(System.err::println); + Set afterPipes = pipesFromSelf(); + if (!beforePipes.equals(afterPipes)) { + Set missing = new HashSet<>(beforePipes); + missing.removeAll(afterPipes); + printPipes(missing, "Missing from beforePipes()"); + Set extra = new HashSet<>(afterPipes); + extra.removeAll(beforePipes); + printPipes(extra, "Extra pipes in afterPipes()"); fail("More or fewer pipes than expected"); } } @@ -157,40 +137,46 @@ public class PipelineLeaksFD { }; } - // Test redirectErrorStream (true/false) has the right number of pipes in use - @EnabledIf("lsofAvailable") + // Test redirectErrorStream (true/false) has the right number of pipes in use. + // Spawn the child to report its pipe inode info. @ParameterizedTest() @MethodSource("redirectCases") void checkRedirectErrorStream(boolean redirectError) { - try (Process p = new ProcessBuilder("cat") + + System.out.println("Using:" + System.getProperty("jdk.lang.Process.launchMechanism")); + + try (Process p = new ProcessBuilder(TEST_JDK + "/bin/java", + "--enable-preview", + "-Djava.library.path=" + JAVA_LIBRARY_PATH, + "--enable-native-access=ALL-UNNAMED", "LinuxFDInfo") .redirectErrorStream(redirectError) .start()) { - System.err.printf("Parent PID; %d, Child Pid: %d\n", MY_PID, p.pid()); - List lsofLines = lsofForPids(MY_PID, p.pid()); - final Set pipes = pipesFromLSOF(lsofLines, MY_PID, p.pid()); - printPipes(pipes, "Parent and child pipes"); - // For each of the child standard pipes, check the parent and child uses of the pipe - var pIn = PipeRecord.pipeFor(pipes, p.pid(), 0).orElseThrow(); - Set pIns = PipeRecord.allSamePipes(pipes, pIn); - assertEquals(2, pIns.size(), "StdIn pipe count"); + final Set pipes = pipesFromSelf(); + printPipes(pipes, "Parent pipes"); + List lines = p.inputReader().readAllLines(); + Set childPipes = lines.stream().map(s -> { + var fdAndInode = LinuxFDInfo.parseFdAndInode(s); + return PipeRecord.lookup(fdAndInode.fd(), "0x%08x".formatted(fdAndInode.inode()), p.pid()); + }) + .collect(Collectors.toCollection(LinkedHashSet::new)); + printPipes(childPipes, "child pipes"); + int uniquePipes = childPipes.stream().map(PipeRecord::myKey).collect(Collectors.toSet()).size(); // Number of pipe references depending on whether stderr is redirected to stdout - long expected = redirectError ? 3 : 2; - - var pOut = PipeRecord.pipeFor(pipes, p.pid(), 1).orElseThrow(); - Set pOuts = PipeRecord.allSamePipes(pipes, pOut); - assertEquals(expected, pOuts.size(), "StdOut pipe count"); - - var pErr = PipeRecord.pipeFor(pipes, p.pid(), 2).orElseThrow(); - Set pErrs = PipeRecord.allSamePipes(pipes, pErr); - assertEquals(expected, pErrs.size(), "StdErr pipe count"); + long expected = redirectError ? 2 : 3; + assertEquals(expected, uniquePipes, "Wrong number of unique pipes"); String expectedTypeName = redirectError ? "java.lang.ProcessBuilder$NullInputStream" : "java.lang.ProcessImpl$ProcessPipeInputStream"; assertEquals(expectedTypeName, p.getErrorStream().getClass().getName(), "errorStream type is incorrect"); + + final Set afterPipes = pipesFromSelf(); + if (!pipes.equals(afterPipes)) { + printPipes(afterPipes, "Parent pipes after are different"); + } } catch (IOException ioe) { fail("Process start", ioe); } @@ -207,122 +193,26 @@ public class PipelineLeaksFD { if (st != 0) { System.err.println("non-zero exit status: " + p); } - } catch (InterruptedException ie) { + } catch (InterruptedException ignore) { } } - /** - * Extract the PipeRecords from the `lsof` output for a list of processes - * - * @param lines lines of lsof output - * @param pids varargs list of pids of interest - * @return a Set of PipeRecords for those pids - */ - private static LinkedHashSet pipesFromLSOF(List lines, long... pids) { - Arrays.sort(pids); - return lines.stream() - .map(PipelineLeaksFD::pipeFromLSOF) - .filter(pr -> pr != null && - Arrays.binarySearch(pids, pr.pid()) >= 0) - .collect(Collectors.toCollection(LinkedHashSet::new)); - } - - /** - * Collect the output of `lsof` for all files. - * Files are used for `lsof` input and output to avoid creating pipes. - * @param pids zero or more pids to request data for; none -> all - * @return a List of lines output from `lsof`. - */ - private static List lsofForPids(long... pids) throws IOException { - Path tmpDir = Path.of("."); - String tmpPrefix = "lsof-"; - Path lsofEmptyInput = Files.createTempFile(tmpDir, tmpPrefix, ".empty"); - Path lsofOutput = Files.createTempFile(tmpDir, tmpPrefix, ".tmp"); - - List lsofArgs = new ArrayList<>(); - lsofArgs.add("lsof"); - if (pids.length > 0) { - StringJoiner pidsArg = new StringJoiner(","); - for (long p : pids) { - pidsArg.add(Long.toString(p)); + static Set pipesFromSelf() { + Set pipes = new LinkedHashSet<>(MAX_FD); + for (int fd = 0; fd < MAX_FD; fd++) { + long inode = LinuxFDInfo.getPipeInodeNum(fd); + if (inode != 0) { + pipes.add(PipeRecord.lookup(fd, "0x%08x".formatted(inode), MY_PID)); } - lsofArgs.add("-p"); - lsofArgs.add(pidsArg.toString()); } - - try (Process p = new ProcessBuilder(lsofArgs) - .redirectOutput(lsofOutput.toFile()) - .redirectInput(lsofEmptyInput.toFile()) // empty input - .redirectError(ProcessBuilder.Redirect.DISCARD) // ignored output - .start()) { - boolean status = p.waitFor(Utils.adjustTimeout(120), TimeUnit.SECONDS); - if (!status) { - p.destroyForcibly(); - } - assertTrue(status, "Process 'lsof' failed"); - - return Files.readAllLines(lsofOutput); - } catch (InterruptedException ie) { - throw new IOException("Waiting for lsof exit interrupted", ie); - } - } - - // Return pipe records by parsing the appropriate platform specific `lsof` output. - static PipeRecord pipeFromLSOF(String s) { - return switch (OS_NAME) { - case "Linux" -> pipeFromLinuxLSOF(s); - case "Mac OS X" -> pipeFromMacLSOF(s); - default -> throw new RuntimeException("lsof not supported on platform: " + OS_NAME); - }; - } - - // Return Pipe from lsof output put, or null (on Mac OS X) - // lsof 55221 xxxx 0 PIPE 0xc76402237956a5cb 16384 ->0xfcb0c07ae447908c - // lsof 55221 xxxx 1 PIPE 0xb486e02f86da463e 16384 ->0xf94eacc85896b4e6 - static PipeRecord pipeFromMacLSOF(String s) { - String[] fields = s.split("\\s+"); - if ("PIPE".equals(fields[4])) { - final int pid = Integer.parseInt(fields[1]); - final String myKey = (fields.length > 5) ? fields[5] : ""; - final String otherKey = (fields.length > 7) ? fields[7].substring(2) : ""; - return PipeRecord.lookup(Integer.parseInt(fields[3]), myKey, otherKey, pid); - } - return null; - } - - // Return Pipe from lsof output put, or null (on Linux) - // java 7612 xxxx 14w FIFO 0,12 0t0 117662267 pipe - // java 7612 xxxx 15r FIFO 0,12 0t0 117662268 pipe - static PipeRecord pipeFromLinuxLSOF(String s) { - String[] fields = s.split("\\s+"); - if ("FIFO".equals(fields[4])) { - final int pid = Integer.parseInt(fields[1]); - final String key = (fields.length > 7) ? fields[7] : ""; - final int fdNum = Integer.parseInt(fields[3].substring(0, fields[3].length() - 1)); - return PipeRecord.lookup(fdNum, key, null, pid); - } - return null; + return pipes; } // Identify a pipe by pid, fd, and a key (unique across processes) // Mac OS X has separate keys for read and write sides, both are matched to the same "name" record PipeRecord(long pid, int fd, KeyedString myKey) { - static PipeRecord lookup(int fd, String myKey, String otherKey, int pid) { - return new PipeRecord(pid, fd, KeyedString.getKey(myKey, otherKey)); - } - - // Return the PipeRecord matching the fd and pid - static Optional pipeFor(Set pipes, long pid, int fd) { - return pipes.stream() - .filter(p -> p.fd() == fd && p.pid() == pid) - .findFirst(); - } - - // Return all the PipeRecords with the same key (the same OS pipe identification) - static Set allSamePipes(Set pipes, PipeRecord p) { - return pipes.stream() - .filter(p1 -> p1.myKey().key.equals(p.myKey().key)) - .collect(Collectors.toSet()); + static PipeRecord lookup(int fd, String myKey, long pid) { + return new PipeRecord(pid, fd, KeyedString.getKey(myKey)); } } @@ -343,12 +233,8 @@ public class PipelineLeaksFD { this(s, k); } - static KeyedString getKey(String key, String otherKey) { - var k = map.computeIfAbsent(key, KeyedString::new); - if (otherKey != null) { - map.putIfAbsent(otherKey, k); - } - return k; + static KeyedString getKey(String key) { + return map.computeIfAbsent(key, KeyedString::new); } public String toString() { diff --git a/test/jdk/java/lang/ProcessBuilder/PipesCloseOnExecTest/PipesCloseOnExecTest.java b/test/jdk/java/lang/ProcessBuilder/PipesCloseOnExecTest/PipesCloseOnExecTest.java new file mode 100644 index 00000000000..1e988f0496c --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/PipesCloseOnExecTest/PipesCloseOnExecTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corp. + * 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 id=FORK + * @bug 8377907 + * @summary Check that we don't open pipes without CLOEXCEC + * @requires os.family == "linux" + * @requires vm.flagless + * @library /test/lib + * @run main/othervm/native -Djdk.lang.Process.launchMechanism=FORK PipesCloseOnExecTest + */ + +/* + * @test id=VFORK + * @bug 8377907 + * @summary Check that we don't open pipes without CLOEXCEC + * @requires os.family == "linux" + * @requires vm.flagless + * @library /test/lib + * @run main/othervm/native -Djdk.lang.Process.launchMechanism=VFORK PipesCloseOnExecTest + */ + +/* + * @test id=POSIX_SPAWN + * @bug 8377907 + * @summary Check that we don't open pipes without CLOEXCEC + * @requires os.family == "linux" + * @requires vm.flagless + * @library /test/lib + * @run main/othervm/native -Djdk.lang.Process.launchMechanism=POSIX_SPAWN PipesCloseOnExecTest + */ + +import jdk.test.lib.process.OutputAnalyzer; + +import java.io.IOException; +import java.time.LocalTime; + +public class PipesCloseOnExecTest { + + // How this works: + // - We start a child process A. Does not matter what, we just call "/bin/date". + // - Concurrently, we (natively, continuously) iterate all pipe file descriptors in + // the process and count all those that are not tagged with CLOEXEC. Finding one + // counts as error. + + // Note that this test can only reliably succeed with Linux and the xxxBSDs, where + // we have pipe2(2). + // + // On MacOs and AIX, we emulate pipe2(2) with pipe(2) and fcntl(2); therefore we + // have a tiny time window in which a concurrent thread can can observe pipe + // filedescriptors without CLOEXEC. Furthermore, on MacOS, we also have to employ + // the double-dup-trick to workaround a the buggy MacOS implementation of posix_spawn. + // Therefore, on these platforms, the test would (correctly) spot "bad" file descriptors. + + native static boolean startTester(); + native static boolean stopTester(); + + static final int num_tries = 100; + + static void printOpenFileDescriptors() { + long mypid = ProcessHandle.current().pid(); + try(Process p = new ProcessBuilder("lsof", "-p", Long.toString(mypid)) + .inheritIO().start()) { + p.waitFor(); + } catch (InterruptedException | IOException ignored) { + // Quietly swallow; it was just an attempt. + } + } + + public static void main(String[] args) throws Exception { + + System.out.println("jdk.lang.Process.launchMechanism=" + + System.getProperty("jdk.lang.Process.launchMechanism")); + + System.loadLibrary("PipesCloseOnExec"); + + if (!startTester()) { + throw new RuntimeException("Failed to start testers (see stdout)"); + } + + System.out.println(LocalTime.now() + ": Call ProcessBuilder.start..."); + + for (int i = 0; i < num_tries; i ++) { + ProcessBuilder pb = new ProcessBuilder("true").inheritIO(); + new OutputAnalyzer(pb.start()).shouldHaveExitValue(0); + } + + if (!stopTester()) { + printOpenFileDescriptors(); + throw new RuntimeException("Catched FDs without CLOEXEC? Check output."); + } + } +} diff --git a/test/jdk/java/lang/ProcessBuilder/PipesCloseOnExecTest/libPipesCloseOnExec.c b/test/jdk/java/lang/ProcessBuilder/PipesCloseOnExecTest/libPipesCloseOnExec.c new file mode 100644 index 00000000000..9a090f3bbd5 --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/PipesCloseOnExecTest/libPipesCloseOnExec.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corp. + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "testlib_thread_barriers.h" + +static void trc(const char* fmt, ...) { + char buf [1024]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + printf("%s\n", buf); + fflush(stdout); +} + +/* Set 1 to restrict this test to pipes, 0 to test all file descriptors. + * (for now, we ignore regular files opened with CLOEXEC since loaded jars seem not tagged as CLOEXEc. + * We should probably fix that eventually. */ +#define TEST_PIPES_ONLY 1 + +/* stdin/out/err file descriptors are usually not CLOEXEC */ +#define IGNORE_BELOW 4 + +/* Only query file descriptors up to this point */ +#define MAX_FD 1024 + +static pthread_t tid_tester; + +static pthread_barrier_t start_barrier; +static atomic_bool stop_now = false; + +/* Mainly to prevent tracing the same fd over and over again: + * 1 - present, 2 - present, cloexec */ +static unsigned fd_state[MAX_FD]; + +static bool is_pipe(int fd) { + struct stat mystat; + if (fstat(fd, &mystat) == -1) { + return false; + } + return mystat.st_mode & S_IFIFO; +} + +static void print_fd_details(int fd, char* out, size_t outlen) { + const char* type = "unknown"; + char link[1024] = { 0 }; + char procfd[129]; + struct stat mystat; + + out[0] = '\0'; + + if (fstat(fd, &mystat) == -1) { + snprintf(out, outlen, "%s", errno == EBADF ? "EBADF" : "???"); + return; + } + + switch (mystat.st_mode & S_IFMT) { + case S_IFBLK: type = "blk"; break; + case S_IFCHR: type = "char"; break; + case S_IFDIR: type = "dir"; break; + case S_IFIFO: type = "fifo"; break; + case S_IFLNK: type = "lnk"; break; + case S_IFREG: type = "reg"; break; + case S_IFSOCK: type = "sock"; break; + } + + snprintf(procfd, sizeof(procfd) - 1, "/proc/self/fd/%d", fd); + int linklen = readlink(procfd, link, sizeof(link) - 1); + if (linklen > 0) { + link[linklen] = '\0'; + snprintf(out, outlen, "%s (%s)", type, link); + } else { + snprintf(out, outlen, "%s", type); + } +} + +/* Returns true for error */ +static bool testFD(int fd) { + + int rc = fcntl(fd, F_GETFD); + if (rc == -1) { + return false; + } + + const bool has_cloexec = (rc & FD_CLOEXEC); + const bool is_a_pipe = is_pipe(fd); + const unsigned state = has_cloexec ? 2 : 1; + bool had_error = false; + + if (fd_state[fd] != state) { + fd_state[fd] = state; + char buf[1024]; + print_fd_details(fd, buf, sizeof(buf)); + if (has_cloexec) { + trc("%d: %s", fd, buf); + } else { + if (fd < IGNORE_BELOW) { + trc("%d: %s ** CLOEXEC MISSING ** (ignored - below scanned range)", fd, buf); + } else if (TEST_PIPES_ONLY && !is_a_pipe) { + trc("%d: %s ** CLOEXEC MISSING ** (ignored - not a pipe)", fd, buf); + } else { + trc("%d: %s ** CLOEXEC MISSING ** (ERROR)", fd, buf); + had_error = true; + } + } + } + + return had_error; +} + +static void* testerLoop(void* dummy) { + + pthread_barrier_wait(&start_barrier); + + trc("Tester is alive"); + + bool had_error = false; + + while (!atomic_load(&stop_now)) { + for (int fd = 0; fd < MAX_FD; fd++) { + bool rc = testFD(fd); + had_error = had_error || rc; + } + } + + trc("Tester dies"); + + return had_error ? (void*)1 : NULL; +} + +JNIEXPORT jboolean JNICALL +Java_PipesCloseOnExecTest_startTester(JNIEnv* env, jclass cls) +{ + pthread_attr_t attr; + int rc = 0; + + if (pthread_barrier_init(&start_barrier, NULL, 2) != 0) { + trc("pthread_barrier_init failed (%d)", errno); + return false; + } + + pthread_attr_init(&attr); + if (pthread_create(&tid_tester, &attr, testerLoop, NULL) != 0) { + trc("pthread_create failed (%d)", errno); + return JNI_FALSE; + } + + pthread_barrier_wait(&start_barrier); + + trc("Started tester"); + + return JNI_TRUE; +} + +JNIEXPORT jboolean JNICALL +Java_PipesCloseOnExecTest_stopTester(JNIEnv* env, jclass cls) +{ + atomic_store(&stop_now, true); + + void* retval = NULL; + pthread_join(tid_tester, &retval); + pthread_barrier_destroy(&start_barrier); + + return retval == NULL ? JNI_TRUE : JNI_FALSE; +} diff --git a/test/jdk/java/lang/ProcessBuilder/ProcessReaperCCL.java b/test/jdk/java/lang/ProcessBuilder/ProcessReaperCCL.java index 79593b014c3..605441cb996 100644 --- a/test/jdk/java/lang/ProcessBuilder/ProcessReaperCCL.java +++ b/test/jdk/java/lang/ProcessBuilder/ProcessReaperCCL.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -23,9 +23,9 @@ /* * @test - * @bug 8269488 * @summary verify that Process Reaper threads have a null CCL - * @run testng ProcessReaperCCL + * @bug 8269488 + * @run junit ProcessReaperCCL */ import java.io.File; @@ -34,14 +34,13 @@ import java.net.URLClassLoader; import java.util.List; import java.util.concurrent.CompletableFuture; -import org.testng.Assert; -import org.testng.annotations.Test; - +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class ProcessReaperCCL { @Test - static void test() throws Exception { + void test() throws Exception { // create a class loader File dir = new File("."); URL[] urls = new URL[] {dir.toURI().toURL()}; @@ -53,12 +52,12 @@ public class ProcessReaperCCL { Process p = pb.start(); CompletableFuture cf = p.onExit(); int exitValue = cf.get().exitValue(); - Assert.assertEquals(exitValue, 0, "error exit value"); + Assertions.assertEquals(0, exitValue, "error exit value"); // Verify all "Process Reaper" threads have a null CCL for (Thread th : Thread.getAllStackTraces().keySet()) { if (th.getName().startsWith("process reaper")) { - Assert.assertEquals(th.getContextClassLoader(), null, "CCL not null"); + Assertions.assertNull(th.getContextClassLoader(), "CCL not null"); } } } diff --git a/test/jdk/java/lang/ProcessBuilder/ProcessStartLoggingTest.java b/test/jdk/java/lang/ProcessBuilder/ProcessStartLoggingTest.java index e9b2fa6b264..2f2bc3c3cee 100644 --- a/test/jdk/java/lang/ProcessBuilder/ProcessStartLoggingTest.java +++ b/test/jdk/java/lang/ProcessBuilder/ProcessStartLoggingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -34,8 +34,7 @@ import java.util.stream.Stream; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.ParameterizedTest; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.*; /* * @test diff --git a/test/jdk/java/lang/ProcessBuilder/ReaderWriterTest.java b/test/jdk/java/lang/ProcessBuilder/ReaderWriterTest.java index a73451417bf..6bff8d5e592 100644 --- a/test/jdk/java/lang/ProcessBuilder/ReaderWriterTest.java +++ b/test/jdk/java/lang/ProcessBuilder/ReaderWriterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -21,17 +21,10 @@ * questions. */ -import static org.testng.Assert.*; - -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.*; import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.hexdump.HexPrinter; -import jdk.test.lib.hexdump.HexPrinter.Formatters; - import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; @@ -42,20 +35,23 @@ import java.nio.file.Files; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; -import java.util.List; -import java.util.Locale; +import java.util.stream.Stream; import jtreg.SkippedException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; /* * @test * @requires vm.flagless * @library /test/lib - * @build jdk.test.lib.process.ProcessTools jdk.test.lib.hexdump.HexPrinter - * @run testng ReaderWriterTest + * @build jdk.test.lib.process.ProcessTools + * @run junit ReaderWriterTest */ -@Test public class ReaderWriterTest { static final String ASCII = "ASCII: \u0000_A-Z_a-Z_\u007C_\u007D_\u007E_\u007F_;"; @@ -65,13 +61,12 @@ public class ReaderWriterTest { public static final String TESTCHARS = "OneWay: " + ASCII + ISO_8859_1 + FRACTIONS; public static final String ROUND_TRIP_TESTCHARS = "RoundTrip: " + ASCII + ISO_8859_1 + FRACTIONS; - @DataProvider(name="CharsetCases") - static Object[][] charsetCases() { - return new Object[][] { - {"UTF-8"}, - {"ISO8859-1"}, - {"US-ASCII"}, - }; + static Stream charsetCases() { + return Stream.of( + Arguments.of("UTF-8"), + Arguments.of("ISO8859-1"), + Arguments.of("US-ASCII") + ); } /** @@ -93,7 +88,7 @@ public class ReaderWriterTest { if (exitValue != 0) System.out.println("exitValue: " + exitValue); } catch (InterruptedException ie) { - Assert.fail("waitFor interrupted"); + fail("waitFor interrupted"); } } @@ -131,32 +126,28 @@ public class ReaderWriterTest { if (errType == 2 || errType == 3) { pb.redirectErrorStream(true); } - Process p = pb.start(); - // Output has been redirected to a null stream; success is IOException on the write - try { + try (Process p = pb.start()) { + // Output has been redirected to a null stream; success is IOException on the write BufferedWriter wr = p.outputWriter(); - wr.write("X"); - wr.flush(); - Assert.fail("writing to null stream should throw IOException"); - } catch (IOException ioe) { - // Normal, A Null output stream is closed when created. - } + assertThrows(IOException.class, () -> { + wr.write("X"); + wr.flush(); + }); - // InputReader should be empty; and at EOF - BufferedReader inputReader = p.inputReader(); - int ch = inputReader.read(); - Assert.assertEquals(ch, -1, "inputReader not at EOF: ch: " + (char)ch); + // InputReader should be empty; and at EOF + BufferedReader inputReader = p.inputReader(); + int ch = inputReader.read(); + assertEquals(-1, ch, "inputReader not at EOF: ch: " + (char) ch); - // InputReader should be empty; and at EOF - BufferedReader errorReader = p.errorReader(); - ch = errorReader.read(); - Assert.assertEquals(ch, -1, "errorReader not at EOF: ch: " + (char)ch); + // InputReader should be empty; and at EOF + BufferedReader errorReader = p.errorReader(); + ch = errorReader.read(); + assertEquals(-1, ch, "errorReader not at EOF: ch: " + (char) ch); - try { int exitValue = p.waitFor(); if (exitValue != 0) System.out.println("exitValue: " + exitValue); } catch (InterruptedException ie) { - Assert.fail("waitFor interrupted"); + fail("waitFor interrupted"); } } } @@ -181,7 +172,8 @@ public class ReaderWriterTest { * * @param encoding a charset name */ - @Test(dataProvider = "CharsetCases", enabled = true) + @ParameterizedTest + @MethodSource("charsetCases") void testCase(String encoding) throws IOException { Charset cs = null; try { @@ -221,32 +213,10 @@ public class ReaderWriterTest { ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( "ReaderWriterTest$ChildWithCharset"); - Process p = pb.start(); - try { - writeTestChars(p.outputWriter(null)); - Assert.fail("Process.outputWriter(null) did not throw NPE"); - } catch (NullPointerException npe) { - // expected, ignore - } - try { - checkReader(p.inputReader(null), null, "Out"); - Assert.fail("Process.inputReader(null) did not throw NPE"); - } catch (NullPointerException npe) { - // expected, ignore - } - try { - checkReader(p.errorReader(null), null, "Err"); - Assert.fail("Process.errorReader(null) did not throw NPE"); - } catch (NullPointerException npe) { - // expected, ignore - } - - p.destroyForcibly(); - try { - // Collect the exit status to cleanup after the process; but ignore it - p.waitFor(); - } catch (InterruptedException ie) { - // Ignored + try (Process p = pb.start()) { + assertThrows(NullPointerException.class, () -> writeTestChars(p.outputWriter(null))); + assertThrows(NullPointerException.class, () -> checkReader(p.inputReader(null), null, "Out")); + assertThrows(NullPointerException.class, () -> checkReader(p.errorReader(null), null, "Err")); } } @@ -272,7 +242,7 @@ public class ReaderWriterTest { var writer = p.outputWriter(cs); writer = p.outputWriter(cs); // try again with same writer = p.outputWriter(otherCharset); // this should throw - Assert.fail("Process.outputWriter(otherCharset) did not throw IllegalStateException"); + fail("Process.outputWriter(otherCharset) did not throw IllegalStateException"); } catch (IllegalStateException ile) { // expected, ignore System.out.println(ile); @@ -281,7 +251,7 @@ public class ReaderWriterTest { var reader = p.inputReader(cs); reader = p.inputReader(cs); // try again with same reader = p.inputReader(otherCharset); // this should throw - Assert.fail("Process.inputReader(otherCharset) did not throw IllegalStateException"); + fail("Process.inputReader(otherCharset) did not throw IllegalStateException"); } catch (IllegalStateException ile) { // expected, ignore System.out.println(ile); @@ -290,7 +260,7 @@ public class ReaderWriterTest { var reader = p.errorReader(cs); reader = p.errorReader(cs); // try again with same reader = p.errorReader(otherCharset); // this should throw - Assert.fail("Process.errorReader(otherCharset) did not throw IllegalStateException"); + fail("Process.errorReader(otherCharset) did not throw IllegalStateException"); } catch (IllegalStateException ile) { // expected, ignore System.out.println(ile); @@ -320,13 +290,13 @@ public class ReaderWriterTest { String reencoded = cs.decode(bb).toString(); if (!firstline.equals(reencoded)) diffStrings(firstline, reencoded); - assertEquals(firstline, reencoded, label + " Test Chars"); + assertEquals(reencoded, firstline, label + " Test Chars"); bb = cs.encode(ROUND_TRIP_TESTCHARS); reencoded = cs.decode(bb).toString(); if (!secondline.equals(reencoded)) diffStrings(secondline, reencoded); - assertEquals(secondline, reencoded, label + " Round Trip Test Chars"); + assertEquals(reencoded, secondline, label + " Round Trip Test Chars"); } } diff --git a/test/jdk/java/lang/ProcessBuilder/libLinuxFDInfo.c b/test/jdk/java/lang/ProcessBuilder/libLinuxFDInfo.c new file mode 100644 index 00000000000..3eb4bd15aac --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/libLinuxFDInfo.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2026, 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. + */ + +#include "jni.h" +#include + +// If the file descriptor is a PIPE, return the INode. +JNIEXPORT jlong JNICALL +Java_LinuxFDInfo_getPipeInodeNum(JNIEnv *env, jclass cls, jint fd) { +#ifdef S_ISFIFO + struct stat buf; + if (fstat(fd, &buf) != -1 && S_ISFIFO(buf.st_mode)) { + return buf.st_ino; + } +#endif + return 0; +} diff --git a/test/jdk/java/lang/ProcessHandle/Basic.java b/test/jdk/java/lang/ProcessHandle/Basic.java index 09674814283..d0342b35a23 100644 --- a/test/jdk/java/lang/ProcessHandle/Basic.java +++ b/test/jdk/java/lang/ProcessHandle/Basic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -21,21 +21,22 @@ * questions. */ -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; -import org.testng.TestNG; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; /* * @test + * @summary Basic tests for ProcessHandler * @library /test/lib * @modules java.base/jdk.internal.misc * jdk.management @@ -45,20 +46,18 @@ import org.testng.annotations.Test; * jdk.test.lib.JDKToolLauncher * jdk.test.lib.Platform * jdk.test.lib.process.* - * @run testng Basic - * @summary Basic tests for ProcessHandler - * @author Roger Riggs + * @run junit Basic */ public class Basic { /** * Tests of ProcessHandle.current. */ @Test - public static void test1() { + public void test1() { try { ProcessHandle self = ProcessHandle.current(); ProcessHandle self1 = ProcessHandle.current(); - assertEquals(self, self1); //, "get pid twice should be same %d: %d"); + assertEquals(self1, self); //, "get pid twice should be same %d: %d"); } finally { // Cleanup any left over processes ProcessHandle.current().children().forEach(ProcessHandle::destroy); @@ -69,16 +68,16 @@ public class Basic { * Tests of ProcessHandle.get. */ @Test - public static void test2() { + public void test2() { try { ProcessHandle self = ProcessHandle.current(); long pid = self.pid(); // known native process id Optional self1 = ProcessHandle.of(pid); - assertEquals(self1.get(), self, + assertEquals(self, self1.get(), "ProcessHandle.of(x.pid()) should be equal pid() %d: %d"); Optional ph = ProcessHandle.of(pid); - assertEquals(pid, ph.get().pid()); + assertEquals(ph.get().pid(), pid); } finally { // Cleanup any left over processes ProcessHandle.current().children().forEach(ProcessHandle::destroy); @@ -86,7 +85,7 @@ public class Basic { } @Test - public static void test3() { + public void test3() { // Test can get parent of current ProcessHandle ph = ProcessHandle.current(); try { @@ -99,7 +98,7 @@ public class Basic { } @Test - public static void test4() { + public void test4() { try { Process p = new ProcessBuilder("sleep", "0").start(); p.waitFor(); @@ -118,7 +117,7 @@ public class Basic { } @Test - public static void test5() { + public void test5() { // Always contains itself. ProcessHandle current = ProcessHandle.current(); List list = ProcessHandle.allProcesses().collect(Collectors.toList()); @@ -131,23 +130,17 @@ public class Basic { } } - @Test(expectedExceptions = IllegalStateException.class) - public static void test6() { - ProcessHandle.current().onExit(); + @Test + public void test6() { + Assertions.assertThrows(IllegalStateException.class, () -> { + ProcessHandle.current().onExit(); + }); } - @Test(expectedExceptions = IllegalStateException.class) - public static void test7() { - ProcessHandle.current().destroyForcibly(); + @Test + public void test7() { + Assertions.assertThrows(IllegalStateException.class, () -> { + ProcessHandle.current().destroyForcibly(); + }); } - - // Main can be used to run the tests from the command line with only testng.jar. - @SuppressWarnings("raw_types") - public static void main(String[] args) { - Class[] testclass = {TreeTest.class}; - TestNG testng = new TestNG(); - testng.setTestClasses(testclass); - testng.run(); - } - } diff --git a/test/jdk/java/lang/ProcessHandle/InfoTest.java b/test/jdk/java/lang/ProcessHandle/InfoTest.java index 38c86db3b91..ea2739e14a7 100644 --- a/test/jdk/java/lang/ProcessHandle/InfoTest.java +++ b/test/jdk/java/lang/ProcessHandle/InfoTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -39,15 +39,15 @@ import java.util.Optional; import java.util.Random; import java.util.concurrent.TimeUnit; -import org.testng.Assert; -import org.testng.TestNG; -import org.testng.annotations.Test; import jdk.test.lib.Platform; import jdk.test.lib.Utils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; /* * @test + * @summary Functions of ProcessHandle.Info * @bug 8077350 8081566 8081567 8098852 8136597 * @requires os.arch != "riscv64" | !(vm.cpu.features ~= ".*qemu.*") * @library /test/lib @@ -59,8 +59,7 @@ import jdk.test.lib.Utils; * jdk.test.lib.JDKToolLauncher * jdk.test.lib.Platform * jdk.test.lib.process.* - * @run testng InfoTest - * @summary Functions of ProcessHandle.Info + * @run junit InfoTest * @author Roger Riggs */ @@ -82,19 +81,11 @@ public class InfoTest { } } - // Main can be used to run the tests from the command line with only testng.jar. - public static void main(String[] args) { - Class[] testclass = {InfoTest.class}; - TestNG testng = new TestNG(); - testng.setTestClasses(testclass); - testng.run(); - } - /** * Test that cputime used shows up in ProcessHandle.info */ @Test - public static void test1() { + public void test1() { System.out.println("Note: when run in samevm mode the cputime of the " + "test runner is included."); ProcessHandle self = ProcessHandle.current(); @@ -108,7 +99,7 @@ public class InfoTest { System.out.printf(" info: %s%n", info); Optional totalCpu = info.totalCpuDuration(); if (totalCpu.isPresent() && (totalCpu.get().compareTo(someCPU) < 0)) { - Assert.fail("reported cputime less than expected: " + someCPU + ", " + + Assertions.fail("reported cputime less than expected: " + someCPU + ", " + "actual: " + info.totalCpuDuration()); } } @@ -117,7 +108,7 @@ public class InfoTest { * Spawn a child with arguments and check they are visible via the ProcessHandle. */ @Test - public static void test2() { + public void test2() { try { long cpuLoopTime = 100; // 100 ms String[] extraArgs = {"pid", "parent", "stdin"}; @@ -170,7 +161,7 @@ public class InfoTest { String expected = Platform.isWindows() ? javaExe + ".exe" : javaExe; Path expectedPath = Paths.get(expected); Path actualPath = Paths.get(command.get()); - Assert.assertTrue(Files.isSameFile(expectedPath, actualPath), + Assertions.assertTrue(Files.isSameFile(expectedPath, actualPath), "Command: expected: " + javaExe + ", actual: " + command.get()); } @@ -179,22 +170,20 @@ public class InfoTest { int offset = args.length - extraArgs.length; for (int i = 0; i < extraArgs.length; i++) { - Assert.assertEquals(args[offset + i], extraArgs[i], + Assertions.assertEquals(extraArgs[i], args[offset + i], "Actual argument mismatch, index: " + i); } // Now check that the first argument is not the same as the executed command - if (args.length > 0) { - Assert.assertNotEquals(args[0], command, - "First argument should not be the executable: args[0]: " - + args[0] + ", command: " + command); - } + Assertions.assertNotEquals(command, args[0], + "First argument should not be the executable: args[0]: " + + args[0] + ", command: " + command); } if (command.isPresent() && info.arguments().isPresent()) { // If both, 'command' and 'arguments' are present, // 'commandLine' is just the concatenation of the two. - Assert.assertTrue(info.commandLine().isPresent(), + Assertions.assertTrue(info.commandLine().isPresent(), "commandLine() must be available"); String javaExe = System.getProperty("test.jdk") + @@ -204,15 +193,15 @@ public class InfoTest { String commandLine = info.commandLine().get(); String commandLineCmd = commandLine.split(" ")[0]; Path commandLineCmdPath = Paths.get(commandLineCmd); - Assert.assertTrue(Files.isSameFile(commandLineCmdPath, expectedPath), + Assertions.assertTrue(Files.isSameFile(commandLineCmdPath, expectedPath), "commandLine() should start with: " + expectedPath + " but starts with " + commandLineCmdPath); - Assert.assertTrue(commandLine.contains(command.get()), + Assertions.assertTrue(commandLine.contains(command.get()), "commandLine() must contain the command: " + command.get()); List allArgs = p1.getArgs(); for (int i = 1; i < allArgs.size(); i++) { - Assert.assertTrue(commandLine.contains(allArgs.get(i)), + Assertions.assertTrue(commandLine.contains(allArgs.get(i)), "commandLine() must contain argument: " + allArgs.get(i)); } } else if (info.commandLine().isPresent() && @@ -222,14 +211,14 @@ public class InfoTest { String commandLine = info.commandLine().get(); String javaExe = "java" + (Platform.isWindows() ? ".exe" : ""); int pos = commandLine.indexOf(javaExe); - Assert.assertTrue(pos > 0, "commandLine() should at least contain 'java'"); + Assertions.assertTrue(pos > 0, "commandLine() should at least contain 'java'"); pos += javaExe.length() + 1; // +1 for the space after the command List allArgs = p1.getArgs(); // First argument is the command - skip it here as we've already checked that. for (int i = 1; (i < allArgs.size()) && (pos + allArgs.get(i).length() < commandLine.length()); i++) { - Assert.assertTrue(commandLine.contains(allArgs.get(i)), + Assertions.assertTrue(commandLine.contains(allArgs.get(i)), "commandLine() must contain argument: " + allArgs.get(i)); pos += allArgs.get(i).length() + 1; } @@ -242,14 +231,14 @@ public class InfoTest { System.out.printf(" info.totalCPU: %s, childCpuTime: %s, diff: %s%n", totalCPU.toNanos(), childCpuTime.toNanos(), childCpuTime.toNanos() - totalCPU.toNanos()); - Assert.assertTrue(checkEpsilon(childCpuTime, totalCPU, epsilon), + Assertions.assertTrue(checkEpsilon(childCpuTime, totalCPU, epsilon), childCpuTime + " should be within " + epsilon + " of " + totalCPU); } - Assert.assertTrue(totalCPU.toNanos() > 0L, + Assertions.assertTrue(totalCPU.toNanos() > 0L, "total cpu time expected > 0ms, actual: " + totalCPU); long t = Utils.adjustTimeout(10L); // Adjusted timeout seconds - Assert.assertTrue(totalCPU.toNanos() < lastCpu.toNanos() + t * 1_000_000_000L, + Assertions.assertTrue(totalCPU.toNanos() < lastCpu.toNanos() + t * 1_000_000_000L, "total cpu time expected < " + t + " seconds more than previous iteration, actual: " + (totalCPU.toNanos() - lastCpu.toNanos())); @@ -258,18 +247,18 @@ public class InfoTest { if (info.startInstant().isPresent()) { Instant startTime = info.startInstant().get(); - Assert.assertTrue(startTime.isBefore(afterStart), + Assertions.assertTrue(startTime.isBefore(afterStart), "startTime after process spawn completed" + startTime + " + > " + afterStart); } } } p1.sendAction("exit"); - Assert.assertTrue(p1.waitFor(Utils.adjustTimeout(30L), TimeUnit.SECONDS), + Assertions.assertTrue(p1.waitFor(Utils.adjustTimeout(30L), TimeUnit.SECONDS), "timeout waiting for process to terminate"); } catch (IOException | InterruptedException ie) { ie.printStackTrace(System.out); - Assert.fail("unexpected exception", ie); + Assertions.fail("unexpected exception", ie); } finally { // Destroy any children that still exist ProcessUtil.destroyProcessTree(ProcessHandle.current()); @@ -280,7 +269,7 @@ public class InfoTest { * Spawn a child with arguments and check they are visible via the ProcessHandle. */ @Test - public static void test3() { + public void test3() { try { for (long sleepTime : Arrays.asList(Utils.adjustTimeout(30), Utils.adjustTimeout(32))) { Process p = spawn("sleep", String.valueOf(sleepTime)); @@ -302,22 +291,22 @@ public class InfoTest { Path executable = Files.readSymbolicLink(Paths.get("/bin/sleep")); expected = executable.getFileName().toString(); } - Assert.assertTrue(command.endsWith(expected), "Command: expected: \'" + + Assertions.assertTrue(command.endsWith(expected), "Command: expected: \'" + expected + "\', actual: " + command); // Verify the command exists and is executable File exe = new File(command); - Assert.assertTrue(exe.exists(), "command must exist: " + exe); - Assert.assertTrue(exe.canExecute(), "command must be executable: " + exe); + Assertions.assertTrue(exe.exists(), "command must exist: " + exe); + Assertions.assertTrue(exe.canExecute(), "command must be executable: " + exe); } if (info.arguments().isPresent()) { String[] args = info.arguments().get(); if (args.length > 0) { - Assert.assertEquals(args[0], String.valueOf(sleepTime)); + Assertions.assertEquals(String.valueOf(sleepTime), args[0]); } } p.destroy(); - Assert.assertTrue(p.waitFor(Utils.adjustTimeout(30), TimeUnit.SECONDS), + Assertions.assertTrue(p.waitFor(Utils.adjustTimeout(30), TimeUnit.SECONDS), "timeout waiting for process to terminate"); } } catch (IOException | InterruptedException ex) { @@ -332,7 +321,7 @@ public class InfoTest { * Cross check the cputime reported from java.management with that for the current process. */ @Test - public static void test4() { + public void test4() { Duration myCputime1 = ProcessUtil.MXBeanCpuTime(); if (Platform.isAix()) { @@ -367,26 +356,26 @@ public class InfoTest { Objects.toString(total2), myCputime2, myCputime2.minus(total2)); Duration epsilon = Duration.ofMillis(200L); // Epsilon is 200ms. - Assert.assertTrue(checkEpsilon(myCputime1, myCputime2, epsilon), + Assertions.assertTrue(checkEpsilon(myCputime1, myCputime2, epsilon), myCputime1.toNanos() + " should be within " + epsilon + " of " + myCputime2.toNanos()); - Assert.assertTrue(checkEpsilon(total1, total2, epsilon), + Assertions.assertTrue(checkEpsilon(total1, total2, epsilon), total1.toNanos() + " should be within " + epsilon + " of " + total2.toNanos()); - Assert.assertTrue(checkEpsilon(myCputime1, total1, epsilon), + Assertions.assertTrue(checkEpsilon(myCputime1, total1, epsilon), myCputime1.toNanos() + " should be within " + epsilon + " of " + total1.toNanos()); - Assert.assertTrue(checkEpsilon(total1, myCputime2, epsilon), + Assertions.assertTrue(checkEpsilon(total1, myCputime2, epsilon), total1.toNanos() + " should be within " + epsilon + " of " + myCputime2.toNanos()); - Assert.assertTrue(checkEpsilon(myCputime2, total2, epsilon), + Assertions.assertTrue(checkEpsilon(myCputime2, total2, epsilon), myCputime2.toNanos() + " should be within " + epsilon + " of " + total2.toNanos()); } } @Test - public static void test5() { + public void test5() { ProcessHandle self = ProcessHandle.current(); Random r = new Random(); for (int i = 0; i < 30; i++) { @@ -458,13 +447,12 @@ public class InfoTest { return; } String user = info.user().get(); - Assert.assertNotNull(user, "User name"); + Assertions.assertNotNull(user, "User name"); if (Platform.isWindows() && "BUILTIN\\Administrators".equals(whoami)) { int bsi = user.lastIndexOf("\\"); - Assert.assertEquals(bsi == -1 ? user : user.substring(bsi + 1), - System.getProperty("user.name"), "User name"); + Assertions.assertEquals(System.getProperty("user.name"), bsi == -1 ? user : user.substring(bsi + 1), "User name"); } else { - Assert.assertEquals(user, whoami, "User name"); + Assertions.assertEquals(whoami, user, "User name"); } } } diff --git a/test/jdk/java/lang/ProcessHandle/OnExitTest.java b/test/jdk/java/lang/ProcessHandle/OnExitTest.java index 79c696efdfc..45c2b51e398 100644 --- a/test/jdk/java/lang/ProcessHandle/OnExitTest.java +++ b/test/jdk/java/lang/ProcessHandle/OnExitTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -35,37 +35,27 @@ import java.util.concurrent.TimeUnit; import jdk.test.lib.Platform; import jdk.test.lib.Utils; -import org.testng.annotations.Test; -import org.testng.Assert; -import org.testng.TestNG; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; /* * @test + * @summary Functions of Process.onExit and ProcessHandle.onExit * @bug 8333742 * @requires vm.flagless * @library /test/lib * @modules jdk.management * @build jdk.test.lib.Utils - * @run testng OnExitTest - * @summary Functions of Process.onExit and ProcessHandle.onExit - * @author Roger Riggs + * @run junit OnExitTest */ public class OnExitTest extends ProcessUtil { - @SuppressWarnings("raw_types") - public static void main(String[] args) { - Class[] testclass = { OnExitTest.class}; - TestNG testng = new TestNG(); - testng.setTestClasses(testclass); - testng.run(); - } - /** * Basic test of exitValue and onExit. */ @Test - public static void test1() { + public void test1() { try { int[] exitValues = {0, 1, 10, 259}; for (int value : exitValues) { @@ -73,24 +63,24 @@ public class OnExitTest extends ProcessUtil { if (value == 259 && !Platform.isWindows()) continue; Process p = JavaChild.spawn("exit", Integer.toString(value)); CompletableFuture future = p.onExit(); - future.thenAccept( (ph) -> { + future.thenAccept((ph) -> { int actualExitValue = ph.exitValue(); printf(" javaChild done: %s, exitStatus: %d%n", ph, actualExitValue); - Assert.assertEquals(actualExitValue, value, "actualExitValue incorrect"); - Assert.assertEquals(ph, p, "Different Process passed to thenAccept"); + Assertions.assertEquals(value, actualExitValue, "actualExitValue incorrect"); + Assertions.assertEquals(p, ph, "Different Process passed to thenAccept"); }); Process h = future.get(); - Assert.assertEquals(h, p); - Assert.assertEquals(p.exitValue(), value); - Assert.assertFalse(p.isAlive(), "Process should not be alive"); - Assert.assertEquals(p.waitFor(), value); - Assert.assertTrue(p.waitFor(1, TimeUnit.MILLISECONDS), + Assertions.assertEquals(p, h); + Assertions.assertEquals(value, p.exitValue()); + Assertions.assertFalse(p.isAlive(), "Process should not be alive"); + Assertions.assertEquals(value, p.waitFor()); + Assertions.assertTrue(p.waitFor(1, TimeUnit.MILLISECONDS), "waitFor should return true"); } } catch (IOException | InterruptedException | ExecutionException ex) { - Assert.fail(ex.getMessage(), ex); + Assertions.fail(ex.getMessage(), ex); } finally { destroyProcessTree(ProcessHandle.current()); } @@ -101,7 +91,7 @@ public class OnExitTest extends ProcessUtil { * Spawn 1 child to spawn 3 children each with 2 children. */ @Test - public static void test2() { + public void test2() { // Please note (JDK-8284282): // @@ -176,7 +166,7 @@ public class OnExitTest extends ProcessUtil { // Check that each of the spawned processes is included in the children List remaining = new ArrayList<>(children); processes.forEach((p, parent) -> { - Assert.assertTrue(remaining.remove(p), "spawned process should have been in children"); + Assertions.assertTrue(remaining.remove(p), "spawned process should have been in children"); }); // Remove Win32 system spawned conhost.exe processes @@ -204,7 +194,7 @@ public class OnExitTest extends ProcessUtil { // Verify that all 9 exit handlers were called with the correct ProcessHandle processes.forEach((p, parent) -> { ProcessHandle value = completions.get(p).getNow(null); - Assert.assertEquals(p, value, "onExit.get value expected: " + p + Assertions.assertEquals(value, p, "onExit.get value expected: " + p + ", actual: " + value + ": " + p.info()); }); @@ -212,9 +202,9 @@ public class OnExitTest extends ProcessUtil { // Show the status of the original children children.forEach(p -> printProcess(p, "after onExit:")); - Assert.assertEquals(proc.isAlive(), false, "destroyed process is alive:: %s%n" + proc); + Assertions.assertFalse(proc.isAlive(), "destroyed process is alive:: %s%n" + proc); } catch (IOException | InterruptedException ex) { - Assert.fail(ex.getMessage()); + Assertions.fail(ex.getMessage(), ex); } finally { if (procHandle != null) { destroyProcessTree(procHandle); @@ -231,7 +221,7 @@ public class OnExitTest extends ProcessUtil { * Check that (B) does not complete until (A) has exited. */ @Test - public static void peerOnExitTest() { + public void peerOnExitTest() { String line = null; ArrayBlockingQueue alines = new ArrayBlockingQueue<>(100); ArrayBlockingQueue blines = new ArrayBlockingQueue<>(100); @@ -265,9 +255,9 @@ public class OnExitTest extends ProcessUtil { try { line = blines.poll(5L, TimeUnit.SECONDS); } catch (InterruptedException ie) { - Assert.fail("interrupted", ie); + Assertions.fail("interrupted", ie); } - Assert.assertNull(line, "waitpid didn't wait"); + Assertions.assertNull(line, "waitpid didn't wait"); A.toHandle().onExit().thenAccept(p -> { System.out.printf(" A.toHandle().onExit().A info: %s, now: %s%n", @@ -296,16 +286,16 @@ public class OnExitTest extends ProcessUtil { split = getSplitLine(blines); } while (!"waitpid".equals(split[1])); - Assert.assertEquals(split[2], "false", "Process A should not be alive"); + Assertions.assertEquals("false", split[2], "Process A should not be alive"); B.sendAction("exit", 0L); } catch (IOException ioe) { - Assert.fail("unable to start JavaChild B", ioe); + Assertions.fail("unable to start JavaChild B", ioe); } finally { B.destroyForcibly(); } } catch (IOException ioe2) { - Assert.fail("unable to start JavaChild A", ioe2); + Assertions.fail("unable to start JavaChild A", ioe2); } finally { A.destroyForcibly(); } @@ -328,7 +318,7 @@ public class OnExitTest extends ProcessUtil { } return split; } catch (InterruptedException ie) { - Assert.fail("interrupted", ie); + Assertions.fail("interrupted", ie); return null; } } diff --git a/test/jdk/java/lang/ProcessHandle/TreeTest.java b/test/jdk/java/lang/ProcessHandle/TreeTest.java index cec880f1fef..567cfea2895 100644 --- a/test/jdk/java/lang/ProcessHandle/TreeTest.java +++ b/test/jdk/java/lang/ProcessHandle/TreeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -38,12 +38,12 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.test.lib.Utils; -import org.testng.Assert; -import org.testng.TestNG; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; /* * @test + * @summary Test counting and JavaChild.spawning and counting of Processes. * @requires vm.flagless * @library /test/lib * @modules java.base/jdk.internal.misc @@ -54,25 +54,15 @@ import org.testng.annotations.Test; * jdk.test.lib.JDKToolLauncher * jdk.test.lib.Platform * jdk.test.lib.process.* - * @run testng/othervm TreeTest - * @summary Test counting and JavaChild.spawning and counting of Processes. + * @run junit/othervm TreeTest * @author Roger Riggs */ public class TreeTest extends ProcessUtil { - // Main can be used to run the tests from the command line with only testng.jar. - @SuppressWarnings("raw_types") - public static void main(String[] args) { - Class[] testclass = {TreeTest.class}; - TestNG testng = new TestNG(); - testng.setTestClasses(testclass); - testng.run(); - } - /** * Test counting and spawning and counting of Processes. */ @Test - public static void test1() { + public void test1() { final int MAXCHILDREN = 2; List spawned = new ArrayList<>(); @@ -92,14 +82,14 @@ public class TreeTest extends ProcessUtil { spawned.stream() .map(Process::toHandle) .filter(p -> !initialChildren.contains(p)) - .forEach(p -> Assert.fail("Spawned process missing from children: " + p)); + .forEach(p -> Assertions.fail("Spawned process missing from children: " + p)); // Send exit command to each spawned Process spawned.forEach(p -> { try { p.sendAction("exit", ""); } catch (IOException ex) { - Assert.fail("IOException in sendAction", ex); + Assertions.fail("IOException in sendAction", ex); } }); @@ -107,7 +97,7 @@ public class TreeTest extends ProcessUtil { spawned.forEach(p -> { do { try { - Assert.assertEquals(p.waitFor(), 0, "exit status incorrect"); + Assertions.assertEquals(0, p.waitFor(), "exit status incorrect"); break; } catch (InterruptedException ex) { continue; // Retry @@ -118,7 +108,7 @@ public class TreeTest extends ProcessUtil { // Verify that ProcessHandle.isAlive sees each of them as not alive for (Process p : spawned) { ProcessHandle ph = p.toHandle(); - Assert.assertFalse(ph.isAlive(), + Assertions.assertFalse(ph.isAlive(), "ProcessHandle.isAlive for exited process: " + ph); } @@ -126,13 +116,13 @@ public class TreeTest extends ProcessUtil { final List afterChildren = getChildren(self); spawned.stream() .map(Process::toHandle) - .filter(p -> afterChildren.contains(p)) - .forEach(p -> Assert.fail("Spawned process missing from children: " + p)); + .filter(afterChildren::contains) + .forEach(p -> Assertions.fail("Spawned process missing from children: " + p)); } catch (IOException ioe) { - Assert.fail("unable to spawn process", ioe); + Assertions.fail("unable to spawn process", ioe); } finally { - // Cleanup any left over processes + // Cleanup any leftover processes spawned.stream() .map(Process::toHandle) .filter(ProcessHandle::isAlive) @@ -147,7 +137,7 @@ public class TreeTest extends ProcessUtil { * Test counting and spawning and counting of Processes. */ @Test - public static void test2() { + public void test2() { try { ConcurrentHashMap processes = new ConcurrentHashMap<>(); @@ -162,12 +152,12 @@ public class TreeTest extends ProcessUtil { ProcessHandle p1Handle = p1.toHandle(); printf(" p1 pid: %d%n", p1.pid()); - // Gather the PIDs from the output of the spawing process + // Gather the PIDs from the output of the spawning process p1.forEachOutputLine((s) -> { String[] split = s.trim().split(" "); if (split.length == 3 && split[1].equals("spawn")) { - Long child = Long.valueOf(split[2]); - Long parent = Long.valueOf(split[0].split(":")[0]); + long child = Long.parseLong(split[2]); + long parent = Long.parseLong(split[0].split(":")[0]); processes.put(ProcessHandle.of(child).get(), ProcessHandle.of(parent).get()); } }); @@ -179,11 +169,11 @@ public class TreeTest extends ProcessUtil { List subprocesses = waitForAllChildren(p1Handle, spawnNew); Optional p1Start = p1Handle.info().startInstant(); for (ProcessHandle ph : subprocesses) { - Assert.assertTrue(ph.isAlive(), "Child should be alive: " + ph); + Assertions.assertTrue(ph.isAlive(), "Child should be alive: " + ph); // Verify each child was started after the parent ph.info().startInstant() .ifPresent(childStart -> p1Start.ifPresent(parentStart -> { - Assert.assertFalse(childStart.isBefore(parentStart), + Assertions.assertFalse(childStart.isBefore(parentStart), String.format("Child process started before parent: child: %s, parent: %s", childStart, parentStart)); })); @@ -217,8 +207,8 @@ public class TreeTest extends ProcessUtil { // Verify that all spawned children show up in the descendants List processes.forEach((p, parent) -> { - Assert.assertEquals(p.isAlive(), true, "Child should be alive: " + p); - Assert.assertTrue(descendants.contains(p), "Spawned child should be listed in descendants: " + p); + Assertions.assertTrue(p.isAlive(), "Child should be alive: " + p); + Assertions.assertTrue(descendants.contains(p), "Spawned child should be listed in descendants: " + p); }); // Closing JavaChild's InputStream will cause all children to exit @@ -228,13 +218,13 @@ public class TreeTest extends ProcessUtil { try { p.onExit().get(); // wait for the child to exit } catch (ExecutionException e) { - Assert.fail("waiting for process to exit", e); + Assertions.fail("waiting for process to exit", e); } } p1.waitFor(); // wait for spawned process to exit // Verify spawned processes are no longer alive - processes.forEach((ph, parent) -> Assert.assertFalse(ph.isAlive(), + processes.forEach((ph, parent) -> Assertions.assertFalse(ph.isAlive(), "process should not be alive: " + ph)); } catch (IOException | InterruptedException t) { t.printStackTrace(); @@ -250,7 +240,7 @@ public class TreeTest extends ProcessUtil { * After they exit they should no longer be listed by descendants. */ @Test - public static void test3() { + public void test3() { ConcurrentHashMap processes = new ConcurrentHashMap<>(); try { @@ -269,15 +259,15 @@ public class TreeTest extends ProcessUtil { p1.forEachOutputLine((s) -> { String[] split = s.trim().split(" "); if (split.length == 3 && split[1].equals("spawn")) { - Long child = Long.valueOf(split[2]); - Long parent = Long.valueOf(split[0].split(":")[0]); + long child = Long.parseLong(split[2]); + long parent = Long.parseLong(split[0].split(":")[0]); processes.put(ProcessHandle.of(child).get(), ProcessHandle.of(parent).get()); spawnCount.countDown(); } }); // Wait for all the subprocesses to be listed as started - Assert.assertTrue(spawnCount.await(Utils.adjustTimeout(30L), TimeUnit.SECONDS), + Assertions.assertTrue(spawnCount.await(Utils.adjustTimeout(30L), TimeUnit.SECONDS), "Timeout waiting for processes to start"); // Debugging; list descendants that are not expected in processes @@ -290,17 +280,17 @@ public class TreeTest extends ProcessUtil { .filter(ph -> !processes.containsKey(ph)) .forEach(ph1 -> ProcessUtil.printProcess(ph1, "Extra process: ")); ProcessUtil.logTaskList(); - Assert.assertEquals(0, count, "Extra processes in descendants"); + Assertions.assertEquals(0, count, "Extra processes in descendants"); } // Verify that all spawned children are alive, show up in the descendants list // then destroy them processes.forEach((p, parent) -> { - Assert.assertEquals(p.isAlive(), true, "Child should be alive: " + p); - Assert.assertTrue(descendants.contains(p), "Spawned child should be listed in descendants: " + p); + Assertions.assertTrue(p.isAlive(), "Child should be alive: " + p); + Assertions.assertTrue(descendants.contains(p), "Spawned child should be listed in descendants: " + p); p.destroyForcibly(); }); - Assert.assertEquals(processes.size(), newChildren, "Wrong number of children"); + Assertions.assertEquals(newChildren, processes.size(), "Wrong number of children"); // Wait for each of the processes to exit processes.forEach((p, parent) -> { @@ -317,21 +307,21 @@ public class TreeTest extends ProcessUtil { } printf("Timeout waiting for exit of pid %s, parent: %s, info: %s%n", p, parent, p.info()); - Assert.fail("Process still alive: " + p); + Assertions.fail("Process still alive: " + p); }); p1.destroyForcibly(); p1.waitFor(); // Verify that none of the spawned children are still listed by descendants List remaining = getDescendants(self); - Assert.assertFalse(remaining.remove(p1Handle), "Child p1 should have exited"); + Assertions.assertFalse(remaining.remove(p1Handle), "Child p1 should have exited"); remaining = remaining.stream().filter(processes::containsKey).collect(Collectors.toList()); - Assert.assertEquals(remaining.size(), 0, "Subprocess(es) should have exited: " + remaining); + Assertions.assertEquals(0, remaining.size(), "Subprocess(es) should have exited: " + remaining); } catch (IOException ioe) { - Assert.fail("Spawn of subprocess failed", ioe); + Assertions.fail("Spawn of subprocess failed", ioe); } catch (InterruptedException inte) { - Assert.fail("InterruptedException", inte); + Assertions.fail("InterruptedException", inte); } finally { processes.forEach((p, parent) -> { if (p.isAlive()) { @@ -346,7 +336,7 @@ public class TreeTest extends ProcessUtil { * Test (Not really a test) that dumps the list of all Processes. */ @Test - public static void test4() { + public void test4() { printf(" Parent Child Info%n"); Stream s = ProcessHandle.allProcesses(); ProcessHandle[] processes = s.toArray(ProcessHandle[]::new); @@ -384,7 +374,7 @@ public class TreeTest extends ProcessUtil { } printf("%s %7s, %7s, %s%n", indent, p_parent, p, info); } - Assert.assertFalse(fail, "Parents missing from all Processes"); + Assertions.assertFalse(fail, "Parents missing from all Processes"); } @@ -392,7 +382,7 @@ public class TreeTest extends ProcessUtil { * A test for scale; launch a large number (14) of subprocesses. */ @Test - public static void test5() { + public void test5() { ConcurrentHashMap processes = new ConcurrentHashMap<>(); int factor = 2; @@ -421,15 +411,15 @@ public class TreeTest extends ProcessUtil { p1.forEachOutputLine((s) -> { String[] split = s.trim().split(" "); if (split.length == 3 && split[1].equals("spawn")) { - Long child = Long.valueOf(split[2]); - Long parent = Long.valueOf(split[0].split(":")[0]); + long child = Long.parseLong(split[2]); + long parent = Long.parseLong(split[0].split(":")[0]); processes.put(ProcessHandle.of(child).get(), ProcessHandle.of(parent).get()); spawnCount.countDown(); } }); // Wait for all the subprocesses to be listed as started - Assert.assertTrue(spawnCount.await(Utils.adjustTimeout(30L), TimeUnit.SECONDS), + Assertions.assertTrue(spawnCount.await(Utils.adjustTimeout(30L), TimeUnit.SECONDS), "Timeout waiting for processes to start"); // Debugging; list descendants that are not expected in processes @@ -442,30 +432,29 @@ public class TreeTest extends ProcessUtil { .filter(ph -> !processes.containsKey(ph)) .forEach(ph1 -> ProcessUtil.printProcess(ph1, "Extra process: ")); ProcessUtil.logTaskList(); - Assert.assertEquals(0, count, "Extra processes in descendants"); + Assertions.assertEquals(0, count, "Extra processes in descendants"); } List subprocesses = getChildren(p1Handle); printf(" children: %s%n", - subprocesses.stream().map(p -> p.pid()) + subprocesses.stream().map(ProcessHandle::pid) .collect(Collectors.toList())); - Assert.assertEquals(getChildren(p1Handle).size(), - factor, "expected direct children"); + Assertions.assertEquals(factor, getChildren(p1Handle).size(), "expected direct children"); count = getDescendants(p1Handle).size(); long totalChildren = factor * factor * factor + factor * factor + factor; - Assert.assertTrue(count >= totalChildren, + Assertions.assertTrue(count >= totalChildren, "expected at least " + totalChildren + ", actual: " + count); List descSubprocesses = getDescendants(p1Handle); printf(" descendants: %s%n", - descSubprocesses.stream().map(p -> p.pid()) + descSubprocesses.stream().map(ProcessHandle::pid) .collect(Collectors.toList())); p1.getOutputStream().close(); // Close stdin for the controlling p1 p1.waitFor(); } catch (InterruptedException | IOException ex) { - Assert.fail("Unexpected Exception", ex); + Assertions.fail("Unexpected Exception", ex); } finally { printf("Duration: %s%n", Duration.between(start, Instant.now())); if (p1 != null) { diff --git a/test/jdk/java/lang/StrictMath/HyperbolicTests.java b/test/jdk/java/lang/StrictMath/HyperbolicTests.java index 4be1b350c1d..9eaed0a081a 100644 --- a/test/jdk/java/lang/StrictMath/HyperbolicTests.java +++ b/test/jdk/java/lang/StrictMath/HyperbolicTests.java @@ -595,6 +595,13 @@ public class HyperbolicTests { {0x1.fffffffffff92p+1, 0x1.0c1f8a6e80edp+1}, {0x1.0108b83c4bbc8p-1, 0x1.ee9c256f3947ep-2}, {-0x1.c41e527b70f43p-3, -0x1.c0863c7dece22p-3}, + + // Julia worst case + {-0x1.02657ff36d5f3p-2, -0x1.ff75bb69b0bf6p-3}, + + // Empirical worst-case points in other libraries with + // larger worst-case errors than FDLIBM + {0x1.0ab3fc30267c2p-1, 0x1.ffd39fc024fbp-2}, }; for (double[] testCase: testCases) { @@ -643,6 +650,15 @@ public class HyperbolicTests { {0x1.ff66e0de4dc6fp+1023, 0x1.633cc2ae1c934p+9}, {0x1.f97ccb0aef314p+11, 0x1.1ff088806d82ep+3}, {0x1.fdf28623ef923p+1021, 0x1.628af341989dap+9}, + + // Julia worst case + {0x1.0001ff6afc4bap+0, 0x1.ffb5238940116p-8}, + + // Empirical worst-case points in other libraries with + // larger worst-case errors than FDLIBM + {0x1.0007fd4307b75p+0, 0x1.ffa704d280935p-7}, + {0x1.071334daf83adp+0, 0x1.e063ca7176ffdp-3}, + {0x1.1d7bc19163966p+0, 0x1.e6db4d68a00dcp-2}, }; for (double[] testCase: testCases) { @@ -709,6 +725,15 @@ public class HyperbolicTests { {0x1.ffffffffffffep-2, 0x1.193ea7aad0309p-1}, {0x1.ffffffffffffbp-1, 0x1.1e9067763b478p+4}, {0x1.ffffffffffffep-1, 0x1.25e4f7b2737fap+4}, + + // Julia worst case + {-0x1.f97fabc0650c4p-4, -0x1.fc16bb2fd3672p-4}, + + // Empirical worst-case points in other libraries with + // larger worst-case errors than FDLIBM + {0x1.ffd834a270fp-10, 0x1.ffd85f432fed2p-10}, + {-0x1.ffbe8dd88527fp-9, -0x1.ffbf38422c2dbp-9}, + {-0x1.e7c1f36602014p-4, -0x1.ea153d6c96817p-4}, }; for (double[] testCase: testCases) diff --git a/test/jdk/java/lang/StrictMath/PowTests.java b/test/jdk/java/lang/StrictMath/PowTests.java index 4b68c5bdcd6..90482b264fe 100644 --- a/test/jdk/java/lang/StrictMath/PowTests.java +++ b/test/jdk/java/lang/StrictMath/PowTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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 @@ -24,6 +24,9 @@ /* * @test * @bug 8136874 8362376 + * @build Tests + * @build PowTests + * @run main PowTests * @summary Tests for StrictMath.pow */ diff --git a/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java b/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java index 1e473ccd974..44276a9f7f5 100644 --- a/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java +++ b/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java @@ -315,7 +315,7 @@ public class GetObjectSizeIntrinsicsTest extends ASimpleInstrumentationTestCase static final int LARGE_OBJ_ARRAY_SIZE = (4096/(int)REF_SIZE)*1024*1024 + 1024; static final boolean CCP = WhiteBox.getWhiteBox().getBooleanVMFlag("UseCompressedClassPointers"); - static final int ARRAY_HEADER_SIZE = CCP ? 16 : (Platform.is64bit() ? 20 : 16); + static final int ARRAY_HEADER_SIZE = CCP ? 16 : (Platform.is64bit() ? 20 : 12); final String mode; diff --git a/test/jdk/java/net/DatagramPacket/ReuseBuf.java b/test/jdk/java/net/DatagramPacket/ReuseBuf.java index 232bd07dded..2213f306a8a 100644 --- a/test/jdk/java/net/DatagramPacket/ReuseBuf.java +++ b/test/jdk/java/net/DatagramPacket/ReuseBuf.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -21,71 +21,132 @@ * questions. */ -/** + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketException; +import java.util.concurrent.atomic.AtomicReference; + +/* * @test - * * @bug 4424096 - * - * @summary DatagramPacket spec needs clarification (reuse buf) + * @summary Verify the specification of DatagramPacket.getData() */ -import java.net.*; - public class ReuseBuf { - static String msgs[] = {"Hello World", "Java", "Good Bye"}; - static int port; + private static final String[] msgs = {"Hello World", "Java", "Good Bye"}; - static class ServerThread extends Thread{ - DatagramSocket ds; - public ServerThread() { + static class Server implements Runnable, AutoCloseable { + + private final DatagramSocket ds; + private volatile boolean closed; + private final AtomicReference serverFailure = new AtomicReference<>(); + + public Server(final InetSocketAddress bindAddr) { try { - InetAddress local = InetAddress.getLocalHost(); - InetSocketAddress bindaddr = new InetSocketAddress(local, 0); - ds = new DatagramSocket(bindaddr); - port = ds.getLocalPort(); + this.ds = new DatagramSocket(bindAddr); + } catch (SocketException e) { + throw new RuntimeException(e); + } + System.out.println("Server bound to address: " + this.ds.getLocalSocketAddress()); + } + + private InetSocketAddress getServerAddress() { + return (InetSocketAddress) this.ds.getLocalSocketAddress(); + } + + private static void serverLog(final String msg) { + System.out.println("[server] " + msg); + } + + @Override + public void run() { + serverLog("server processing started"); + try { + doRun(); } catch (Exception e) { - throw new RuntimeException(e.getMessage()); + // no need to be concerned with the exception + // if the server is already closed + if (!closed) { + this.serverFailure.set(e); + System.err.println("server exception: " + e); + e.printStackTrace(); + } + } finally { + close(); } } - public void run() { - byte b[] = new byte[100]; - DatagramPacket dp = new DatagramPacket(b,b.length); - while (true) { - try { - ds.receive(dp); - String reply = new String(dp.getData(), dp.getOffset(), dp.getLength()); - ds.send(new DatagramPacket(reply.getBytes(),reply.length(), - dp.getAddress(),dp.getPort())); - if (reply.equals(msgs[msgs.length-1])) { - break; - } - } catch (Exception e) { - throw new RuntimeException(e.getMessage()); + private void doRun() throws Exception { + byte[] b = new byte[100]; + DatagramPacket dp = new DatagramPacket(b, b.length); + while (!closed) { + serverLog("waiting to receive a message at " + ds.getLocalSocketAddress()); + ds.receive(dp); + String reply = new String(dp.getData(), dp.getOffset(), dp.getLength()); + serverLog("replying to " + dp.getAddress() + ":" + dp.getPort()); + ds.send(new DatagramPacket(reply.getBytes(), reply.length(), + dp.getAddress(), dp.getPort())); + if (reply.equals(msgs[msgs.length - 1])) { + break; } } - ds.close(); + } + + @Override + public void close() { + if (this.closed) { + return; + } + synchronized (this) { + if (this.closed) { + return; + } + this.closed = true; + } + System.out.println("Server closing " + ds.getLocalSocketAddress()); + this.ds.close(); } } - public static void main(String args[]) throws Exception { - ServerThread st = new ServerThread(); - st.start(); - InetAddress local = InetAddress.getLocalHost(); - InetSocketAddress bindaddr = new InetSocketAddress(local, 0); - DatagramSocket ds = new DatagramSocket(bindaddr); - byte b[] = new byte[100]; - DatagramPacket dp = new DatagramPacket(b,b.length); - for (int i = 0; i < msgs.length; i++) { - ds.send(new DatagramPacket(msgs[i].getBytes(),msgs[i].length(), - InetAddress.getLocalHost(), - port)); - ds.receive(dp); - if (!msgs[i].equals(new String(dp.getData(), dp.getOffset(), dp.getLength()))) { - throw new RuntimeException("Msg expected: "+msgs[i] +msgs[i].length()+ - "msg received: "+new String(dp.getData(), dp.getOffset(), dp.getLength())+dp.getLength()); + public static void main(String[] args) throws Exception { + InetSocketAddress loopbackEphemeral = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); + Server server; + Thread serverThread; + try (var _ = server = new Server(loopbackEphemeral); + DatagramSocket ds = new DatagramSocket(loopbackEphemeral)) { + + InetSocketAddress destAddr = server.getServerAddress(); + // start the server + serverThread = new Thread(server); + serverThread.start(); + + byte[] b = new byte[100]; + DatagramPacket dp = new DatagramPacket(b, b.length); + for (String msg : msgs) { + System.out.println("sending message from " + ds.getLocalSocketAddress() + + " to " + destAddr); + ds.send(new DatagramPacket(msg.getBytes(), msg.length(), destAddr)); + // wait for a reply from the server + ds.receive(dp); + System.out.println("received message from: " + dp.getAddress() + ":" + dp.getPort()); + String actual = new String(dp.getData(), dp.getOffset(), dp.getLength()); + if (!msg.equals(actual)) { + throw new RuntimeException("Msg expected: " + msg + + " of length: " + msg.length() + + ", actual received: " + actual + " of length: " + dp.getLength()); + } } + System.out.println("All " + msgs.length + " replies received from the server"); + } + // wait for the server thread to complete + System.out.println("awaiting server thread " + serverThread + " to complete"); + serverThread.join(); + Exception serverFailure = server.serverFailure.get(); + if (serverFailure != null) { + System.err.println("Unexpected failure on server: " + serverFailure); + throw serverFailure; } - ds.close(); - System.out.println("Test Passed!!!"); } } diff --git a/test/jdk/java/net/URL/Constructor.java b/test/jdk/java/net/URL/Constructor.java index 13eae40d6d4..b1d6dc91bc4 100644 --- a/test/jdk/java/net/URL/Constructor.java +++ b/test/jdk/java/net/URL/Constructor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -48,6 +48,7 @@ public class Constructor { entries.addAll(Arrays.asList(jarURLs)); entries.addAll(Arrays.asList(normalHttpURLs)); entries.addAll(Arrays.asList(abnormalHttpURLs)); + entries.addAll(Arrays.asList(blankComponents)); if (hasFtp()) entries.addAll(Arrays.asList(ftpURLs)); URL url; @@ -252,4 +253,20 @@ public class Constructor { "/dir1/entry.txt", "ftp://br:pwd@ftp.foo.com/dir1/entry.txt") }; + + static Entry[] blankComponents = new Entry[] { + new Entry(null, "http://host/path#", "http://host/path#"), + new Entry(null, "http://host/path?", "http://host/path?"), + new Entry(null, "http://host/path?#", "http://host/path?#"), + new Entry(null, "http://host/path#?", "http://host/path#?"), + new Entry(null, "file:/path#", "file:/path#"), + new Entry(null, "file:/path?", "file:/path?"), + new Entry(null, "file:/path?#", "file:/path?#"), + new Entry(null, "file:///path#", "file:/path#"), + new Entry(null, "file:///path?", "file:/path?"), + new Entry(null, "file:/path#?", "file:/path#?"), + new Entry("file:/path", "path?#", "file:/path?#"), + new Entry(null, "file:", "file:"), + new Entry(null, "file:#", "file:#") + }; } diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ByteBufferUtils.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ByteBufferUtils.java index 29276dc7b60..0f9d6a373d4 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ByteBufferUtils.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ByteBufferUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -61,7 +61,7 @@ public final class ByteBufferUtils { private static byte[] bytes(ByteBuffer buffer) { byte[] bytes = new byte[buffer.limit()]; - buffer.get(bytes); + buffer.get(buffer.position(), bytes); return bytes; } diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/FromPublisherTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/FromPublisherTest.java index f05eab0a2e5..7d3e78e0b29 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/FromPublisherTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/FromPublisherTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -25,10 +25,13 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.nio.ByteBuffer; +import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; +import static java.net.http.HttpRequest.BodyPublishers.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -37,16 +40,18 @@ import static org.junit.jupiter.api.Assertions.assertTrue; * @test * @bug 8364733 * @summary Verify all specified `HttpRequest.BodyPublishers::fromPublisher` behavior - * @build RecordingSubscriber - * @run junit FromPublisherTest + * @build ByteBufferUtils + * RecordingSubscriber + * ReplayTestSupport + * @run junit ${test.main.class} */ -class FromPublisherTest { +class FromPublisherTest extends ReplayTestSupport { @Test void testNullPublisher() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.fromPublisher(null)); - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.fromPublisher(null, 1)); + assertThrows(NullPointerException.class, () -> fromPublisher(null)); + assertThrows(NullPointerException.class, () -> fromPublisher(null, 1)); } @ParameterizedTest @@ -54,7 +59,7 @@ class FromPublisherTest { void testInvalidContentLength(long contentLength) { IllegalArgumentException exception = assertThrows( IllegalArgumentException.class, - () -> HttpRequest.BodyPublishers.fromPublisher(null, contentLength)); + () -> fromPublisher(null, contentLength)); String exceptionMessage = exception.getMessage(); assertTrue( exceptionMessage.contains("non-positive contentLength"), @@ -64,29 +69,26 @@ class FromPublisherTest { @ParameterizedTest @ValueSource(longs = {1, 2, 3, 4}) void testValidContentLength(long contentLength) { - HttpRequest.BodyPublisher publisher = - HttpRequest.BodyPublishers.fromPublisher(HttpRequest.BodyPublishers.noBody(), contentLength); + BodyPublisher publisher = fromPublisher(noBody(), contentLength); assertEquals(contentLength, publisher.contentLength()); } @Test void testNoContentLength() { - HttpRequest.BodyPublisher publisher = - HttpRequest.BodyPublishers.fromPublisher(HttpRequest.BodyPublishers.noBody()); + BodyPublisher publisher = fromPublisher(noBody()); assertEquals(-1, publisher.contentLength()); } @Test void testNullSubscriber() { - HttpRequest.BodyPublisher publisher = - HttpRequest.BodyPublishers.fromPublisher(HttpRequest.BodyPublishers.noBody()); + BodyPublisher publisher = fromPublisher(noBody()); assertThrows(NullPointerException.class, () -> publisher.subscribe(null)); } @Test void testDelegation() throws InterruptedException { BlockingQueue publisherInvocations = new LinkedBlockingQueue<>(); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.fromPublisher(subscriber -> { + BodyPublisher publisher = fromPublisher(subscriber -> { publisherInvocations.add("subscribe"); publisherInvocations.add(subscriber); }); @@ -97,4 +99,13 @@ class FromPublisherTest { assertTrue(subscriber.invocations.isEmpty()); } + @Override + Iterable createReplayTargets() { + byte[] content = ByteBufferUtils.byteArrayOfLength(10); + ByteBuffer expectedBuffer = ByteBuffer.wrap(content); + BodyPublisher delegatePublisher = ofByteArray(content); + BodyPublisher publisher = fromPublisher(delegatePublisher, delegatePublisher.contentLength()); + return List.of(new ReplayTarget(expectedBuffer, publisher)); + } + } diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/NoBodyTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/NoBodyTest.java index f58e9505c9a..d45482700d6 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/NoBodyTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/NoBodyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -23,7 +23,10 @@ import org.junit.jupiter.api.Test; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; +import java.nio.ByteBuffer; +import java.util.List; import java.util.concurrent.Flow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -32,17 +35,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; * @test * @bug 8364733 * @summary Verify all specified `HttpRequest.BodyPublishers::noBody` behavior - * @build RecordingSubscriber - * @run junit NoBodyTest + * @build ByteBufferUtils + * RecordingSubscriber + * ReplayTestSupport + * @run junit ${test.main.class} */ -class NoBodyTest { +class NoBodyTest extends ReplayTestSupport { @Test void test() throws InterruptedException { // Create the publisher - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.noBody(); + BodyPublisher publisher = BodyPublishers.noBody(); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -54,4 +59,11 @@ class NoBodyTest { } + @Override + Iterable createReplayTargets() { + ByteBuffer expectedBuffer = ByteBuffer.wrap(new byte[0]); + BodyPublisher publisher = BodyPublishers.noBody(); + return List.of(new ReplayTarget(expectedBuffer, publisher)); + } + } diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArrayTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArrayTest.java index 19f7369125d..25233c9dd99 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArrayTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArrayTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -25,7 +25,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -39,30 +40,34 @@ import static org.junit.jupiter.api.Assertions.assertThrows; * @test * @bug 8364733 * @summary Verify all specified `HttpRequest.BodyPublishers::ofByteArray` behavior - * @build RecordingSubscriber - * @run junit OfByteArrayTest + * + * @build ByteBufferUtils + * RecordingSubscriber + * ReplayTestSupport + * + * @run junit ${test.main.class} * * @comment Using `main/othervm` to initiate tests that depend on a custom-configured JVM - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking "" 0 0 "" - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking a 0 0 "" - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking a 1 0 "" - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking a 0 1 a - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking ab 0 1 a - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking ab 1 1 b - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking ab 0 2 ab - * @run main/othervm -Djdk.httpclient.bufsize=1 OfByteArrayTest testChunking abc 0 3 a:b:c - * @run main/othervm -Djdk.httpclient.bufsize=2 OfByteArrayTest testChunking abc 0 3 ab:c - * @run main/othervm -Djdk.httpclient.bufsize=2 OfByteArrayTest testChunking abcdef 2 4 cd:ef + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking "" 0 0 "" + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking a 0 0 "" + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking a 1 0 "" + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking a 0 1 a + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking ab 0 1 a + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking ab 1 1 b + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking ab 0 2 ab + * @run main/othervm -Djdk.httpclient.bufsize=1 ${test.main.class} testChunking abc 0 3 a:b:c + * @run main/othervm -Djdk.httpclient.bufsize=2 ${test.main.class} testChunking abc 0 3 ab:c + * @run main/othervm -Djdk.httpclient.bufsize=2 ${test.main.class} testChunking abcdef 2 4 cd:ef */ -public class OfByteArrayTest { +public class OfByteArrayTest extends ReplayTestSupport { private static final Charset CHARSET = StandardCharsets.US_ASCII; @Test void testNullContent() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofByteArray(null)); - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofByteArray(null, 1, 2)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofByteArray(null)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofByteArray(null, 1, 2)); } @ParameterizedTest @@ -78,7 +83,15 @@ public class OfByteArrayTest { byte[] content = contentText.getBytes(CHARSET); assertThrows( IndexOutOfBoundsException.class, - () -> HttpRequest.BodyPublishers.ofByteArray(content, offset, length)); + () -> BodyPublishers.ofByteArray(content, offset, length)); + } + + @Override + Iterable createReplayTargets() { + byte[] content = "this content needs to be replayed again and again".getBytes(CHARSET); + ByteBuffer expectedBuffer = ByteBuffer.wrap(content); + BodyPublisher publisher = BodyPublishers.ofByteArray(content); + return List.of(new ReplayTarget(expectedBuffer, publisher)); } /** @@ -105,7 +118,7 @@ public class OfByteArrayTest { // Create the publisher byte[] content = contentText.getBytes(CHARSET); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArray(content, offset, length); + BodyPublisher publisher = BodyPublishers.ofByteArray(content, offset, length); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArraysTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArraysTest.java index ab051d2020f..10784f7dbee 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArraysTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArraysTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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,8 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Iterator; @@ -46,15 +47,18 @@ import static org.junit.jupiter.api.Assertions.assertThrows; * @test * @bug 8226303 8364733 * @summary Verify all specified `HttpRequest.BodyPublishers::ofByteArrays` behavior + * * @build ByteBufferUtils * RecordingSubscriber + * ReplayTestSupport + * * @run junit OfByteArraysTest * * @comment Using `main/othervm` to initiate tests that depend on a custom-configured JVM * @run main/othervm -Xmx64m OfByteArraysTest testOOM */ -public class OfByteArraysTest { +public class OfByteArraysTest extends ReplayTestSupport { @ParameterizedTest @ValueSource(ints = {0, 1, 2, 3}) @@ -65,7 +69,7 @@ public class OfByteArraysTest { .range(0, length) .mapToObj(i -> new byte[]{(byte) i}) .toList(); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(buffers::iterator); + BodyPublisher publisher = BodyPublishers.ofByteArrays(buffers::iterator); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -95,7 +99,7 @@ public class OfByteArraysTest { case 2 -> List.of(buffer2).iterator(); default -> throw new AssertionError(); }; - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(iterable); + BodyPublisher publisher = BodyPublishers.ofByteArrays(iterable); // Subscribe twice (to force two `Iterable::iterator` invocations) RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -112,14 +116,14 @@ public class OfByteArraysTest { @Test void testNullIterable() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofByteArrays(null)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofByteArrays(null)); } @Test void testNullIterator() throws InterruptedException { // Create the publisher - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(() -> null); + BodyPublisher publisher = BodyPublishers.ofByteArrays(() -> null); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -138,7 +142,7 @@ public class OfByteArraysTest { // Create the publisher List iterable = new ArrayList<>(); iterable.add(null); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(iterable); + BodyPublisher publisher = BodyPublishers.ofByteArrays(iterable); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -156,7 +160,7 @@ public class OfByteArraysTest { // Create the publisher RuntimeException exception = new RuntimeException("failure for `testIteratorCreationException`"); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(() -> { + BodyPublisher publisher = BodyPublishers.ofByteArrays(() -> { throw exception; }); @@ -192,7 +196,7 @@ public class OfByteArraysTest { // Create the publisher IteratorThrowingAtEnd iterator = new IteratorThrowingAtEnd(exceptionIndex, hasNextException, nextException); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(() -> iterator); + BodyPublisher publisher = BodyPublishers.ofByteArrays(() -> iterator); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -257,6 +261,14 @@ public class OfByteArraysTest { } + @Override + Iterable createReplayTargets() { + byte[] byteArray = ByteBufferUtils.byteArrayOfLength(9); + ByteBuffer expectedBuffer = ByteBuffer.wrap(byteArray); + BodyPublisher publisher = BodyPublishers.ofByteArrays(List.of(byteArray)); + return List.of(new ReplayTarget(expectedBuffer, -1, publisher, null)); + } + /** * Initiates tests that depend on a custom-configured JVM. */ @@ -273,7 +285,7 @@ public class OfByteArraysTest { // Create the publisher int length = ByteBufferUtils.findLengthExceedingMaxMemory(); Iterable iterable = createIterableOfLength(length); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(iterable); + BodyPublisher publisher = BodyPublishers.ofByteArrays(iterable); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); diff --git a/test/jdk/java/net/httpclient/FileChannelPublisherTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileChannelTest.java similarity index 96% rename from test/jdk/java/net/httpclient/FileChannelPublisherTest.java rename to test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileChannelTest.java index c9c78791da3..47596f40a5f 100644 --- a/test/jdk/java/net/httpclient/FileChannelPublisherTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileChannelTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -26,9 +26,12 @@ * @summary Verifies `HttpRequest.BodyPublishers::ofFileChannel` * @library /test/lib * /test/jdk/java/net/httpclient/lib - * @build jdk.httpclient.test.lib.common.HttpServerAdapters + * @build ByteBufferUtils + * RecordingSubscriber + * ReplayTestSupport + * jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.test.lib.net.SimpleSSLContext - * @run junit FileChannelPublisherTest + * @run junit ${test.main.class} */ import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestHandler; @@ -52,8 +55,10 @@ import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpClient.Version; import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse; +import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.FileChannel; import java.nio.file.Files; @@ -82,9 +87,9 @@ import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -class FileChannelPublisherTest { +class OfFileChannelTest extends ReplayTestSupport { - private static final String CLASS_NAME = FileChannelPublisherTest.class.getSimpleName(); + private static final String CLASS_NAME = OfFileChannelTest.class.getSimpleName(); private static final Logger LOGGER = Utils.getDebugLogger(CLASS_NAME::toString, Utils.DEBUG); @@ -650,6 +655,22 @@ class FileChannelPublisherTest { } + @Override + Iterable createReplayTargets() { + int fileLength = 42; + byte[] fileBytes = generateFileBytes(fileLength); + Path filePath = Path.of("replayTarget.txt"); + try { + Files.write(filePath, fileBytes, StandardOpenOption.CREATE); + FileChannel fileChannel = FileChannel.open(filePath); + BodyPublisher publisher = BodyPublishers.ofFileChannel(fileChannel, 0, fileLength); + ByteBuffer expectedBuffer = ByteBuffer.wrap(fileBytes); + return List.of(new ReplayTarget(expectedBuffer, fileLength, publisher, fileChannel)); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + } + /** * Performs the initial {@code HEAD} request to the specified server. This * effectively admits a connection to the client's pool, where all protocol diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileTest.java index 94b5a596fc6..7287468d600 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -31,7 +31,8 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; import java.io.UncheckedIOException; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; import java.nio.ByteBuffer; import java.nio.file.FileSystem; import java.nio.file.FileSystems; @@ -51,15 +52,18 @@ import static org.junit.jupiter.api.Assertions.assertTrue; * @test * @bug 8226303 8235459 8358688 8364733 * @summary Verify all specified `HttpRequest.BodyPublishers::ofFile` behavior + * * @build ByteBufferUtils * RecordingSubscriber - * @run junit OfFileTest + * ReplayTestSupport + * + * @run junit ${test.main.class} * * @comment Using `main/othervm` to initiate tests that depend on a custom-configured JVM - * @run main/othervm -Xmx64m OfFileTest testOOM + * @run main/othervm -Xmx64m ${test.main.class} testOOM */ -public class OfFileTest { +public class OfFileTest extends ReplayTestSupport { private static final Path DEFAULT_FS_DIR = Path.of(System.getProperty("user.dir", ".")); @@ -89,14 +93,14 @@ public class OfFileTest { @Test void testNullPath() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofFile(null)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofFile(null)); } @ParameterizedTest @MethodSource("parentDirs") void testNonExistentPath(Path parentDir) { Path nonExistentPath = createFilePath(parentDir, "testNonExistentPath"); - assertThrows(FileNotFoundException.class, () -> HttpRequest.BodyPublishers.ofFile(nonExistentPath)); + assertThrows(FileNotFoundException.class, () -> BodyPublishers.ofFile(nonExistentPath)); } @ParameterizedTest @@ -106,7 +110,7 @@ public class OfFileTest { // Create the publisher byte[] fileBytes = ByteBufferUtils.byteArrayOfLength(3); Path filePath = createFile(parentDir, "testNonExistentPathAtSubscribe", fileBytes); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofFile(filePath); + BodyPublisher publisher = BodyPublishers.ofFile(filePath); // Delete the file Files.delete(filePath); @@ -131,7 +135,7 @@ public class OfFileTest { void testIrregularFile(Path parentDir) throws Exception { // Create the publisher - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofFile(parentDir); + BodyPublisher publisher = BodyPublishers.ofFile(parentDir); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -167,7 +171,7 @@ public class OfFileTest { // Create the publisher byte[] fileBytes = ByteBufferUtils.byteArrayOfLength(BIG_FILE_LENGTH); Path filePath = createFile(parentDir, "testFileModificationWhileReading", fileBytes); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofFile(filePath); + BodyPublisher publisher = BodyPublishers.ofFile(filePath); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -210,7 +214,7 @@ public class OfFileTest { // Create the publisher byte[] fileBytes = ByteBufferUtils.byteArrayOfLength(fileLength); Path filePath = createFile(parentDir, "testFileOfLength", fileBytes); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofFile(filePath); + BodyPublisher publisher = BodyPublishers.ofFile(filePath); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -222,6 +226,24 @@ public class OfFileTest { } + @Override + Iterable createReplayTargets() { + byte[] fileBytes = ByteBufferUtils.byteArrayOfLength(3); + ByteBuffer expectedBuffer = ByteBuffer.wrap(fileBytes); + return parentDirs() + .stream() + .map(parentDir -> { + try { + Path filePath = createFile(parentDir, "replayTest", fileBytes); + BodyPublisher publisher = BodyPublishers.ofFile(filePath); + return new ReplayTarget(expectedBuffer, publisher); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + }) + .toList(); + } + /** * Initiates tests that depend on a custom-configured JVM. */ @@ -248,7 +270,7 @@ public class OfFileTest { // Create the publisher int fileLength = ByteBufferUtils.findLengthExceedingMaxMemory(); Path filePath = createFileOfLength(parentDir, "testOOM", fileLength); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofFile(filePath); + BodyPublisher publisher = BodyPublishers.ofFile(filePath); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfInputStreamTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfInputStreamTest.java index 7688c1674ee..06ed13333ae 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfInputStreamTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfInputStreamTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -28,7 +28,9 @@ import org.junit.jupiter.params.provider.ValueSource; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; +import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.Flow; import java.util.function.Supplier; @@ -41,19 +43,22 @@ import static org.junit.jupiter.api.Assertions.assertThrows; * @test * @bug 8364733 * @summary Verify all specified `HttpRequest.BodyPublishers::ofInputStream` behavior + * * @build ByteBufferUtils * RecordingSubscriber - * @run junit OfInputStreamTest + * ReplayTestSupport + * + * @run junit ${test.main.class} * * @comment Using `main/othervm` to initiate tests that depend on a custom-configured JVM - * @run main/othervm -Xmx64m OfInputStreamTest testOOM + * @run main/othervm -Xmx64m ${test.main.class} testOOM */ -public class OfInputStreamTest { +public class OfInputStreamTest extends ReplayTestSupport { @Test void testNullInputStreamSupplier() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofInputStream(null)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofInputStream(null)); } @Test @@ -61,7 +66,7 @@ public class OfInputStreamTest { // Create the publisher RuntimeException exception = new RuntimeException(); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofInputStream(() -> { throw exception; }); + BodyPublisher publisher = BodyPublishers.ofInputStream(() -> { throw exception; }); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -80,7 +85,7 @@ public class OfInputStreamTest { void testNullInputStream() throws InterruptedException { // Create the publisher - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofInputStream(() -> null); + BodyPublisher publisher = BodyPublishers.ofInputStream(() -> null); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -107,7 +112,7 @@ public class OfInputStreamTest { case 2 -> new ByteArrayInputStream(buffer2); default -> throw new AssertionError(); }; - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofInputStream(inputStreamSupplier); + BodyPublisher publisher = BodyPublishers.ofInputStream(inputStreamSupplier); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -129,7 +134,7 @@ public class OfInputStreamTest { // Create the publisher byte[] content = ByteBufferUtils.byteArrayOfLength(length); InputStream inputStream = new ByteArrayInputStream(content); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofInputStream(() -> inputStream); + BodyPublisher publisher = BodyPublishers.ofInputStream(() -> inputStream); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -148,7 +153,7 @@ public class OfInputStreamTest { // Create the publisher RuntimeException exception = new RuntimeException("failure for `read`"); InputStream inputStream = new InputStreamThrowingOnCompletion(exceptionIndex, exception); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofInputStream(() -> inputStream); + BodyPublisher publisher = BodyPublishers.ofInputStream(() -> inputStream); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -185,6 +190,14 @@ public class OfInputStreamTest { } + @Override + Iterable createReplayTargets() { + byte[] content = ByteBufferUtils.byteArrayOfLength(10); + ByteBuffer expectedBuffer = ByteBuffer.wrap(content); + BodyPublisher publisher = BodyPublishers.ofInputStream(() -> new ByteArrayInputStream(content)); + return List.of(new ReplayTarget(expectedBuffer, -1, publisher, null)); + } + /** * Initiates tests that depend on a custom-configured JVM. */ @@ -200,8 +213,8 @@ public class OfInputStreamTest { // Create the publisher using an `InputStream` that emits content exceeding the maximum memory int length = ByteBufferUtils.findLengthExceedingMaxMemory(); - HttpRequest.BodyPublisher publisher = - HttpRequest.BodyPublishers.ofInputStream(() -> new InputStream() { + BodyPublisher publisher = + BodyPublishers.ofInputStream(() -> new InputStream() { private int position; diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfStringTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfStringTest.java index 143b5ce17da..2e1fcb11f99 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfStringTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfStringTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -26,10 +26,12 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.concurrent.Flow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -41,10 +43,11 @@ import static org.junit.jupiter.api.Assertions.assertThrows; * @summary Verify all specified `HttpRequest.BodyPublishers::ofString` behavior * @build ByteBufferUtils * RecordingSubscriber - * @run junit OfStringTest + * ReplayTestSupport + * @run junit ${test.main.class} */ -class OfStringTest { +class OfStringTest extends ReplayTestSupport { private static final Charset CHARSET = StandardCharsets.US_ASCII; @@ -58,7 +61,7 @@ class OfStringTest { contentChars[i] = (char) ('a' + i); } String content = new String(contentChars); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofString(content, CHARSET); + BodyPublisher publisher = BodyPublishers.ofString(content, CHARSET); // Subscribe assertEquals(length, publisher.contentLength()); @@ -87,7 +90,7 @@ class OfStringTest { void testCharset(String content, Charset charset) throws InterruptedException { // Create the publisher - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofString(content, charset); + BodyPublisher publisher = BodyPublishers.ofString(content, charset); // Subscribe ByteBuffer expectedBuffer = charset.encode(content); @@ -108,12 +111,20 @@ class OfStringTest { @Test void testNullContent() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofString(null, CHARSET)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofString(null, CHARSET)); } @Test void testNullCharset() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofString("foo", null)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofString("foo", null)); + } + + @Override + Iterable createReplayTargets() { + String content = "this content needs to be replayed again and again"; + ByteBuffer expectedBuffer = CHARSET.encode(content); + BodyPublisher publisher = BodyPublishers.ofString(content, CHARSET); + return List.of(new ReplayTarget(expectedBuffer, publisher)); } } diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ReplayTestSupport.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ReplayTestSupport.java new file mode 100644 index 00000000000..788b59e96b7 --- /dev/null +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ReplayTestSupport.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2026, 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 org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.net.http.HttpRequest.BodyPublisher; +import java.nio.ByteBuffer; +import java.util.concurrent.Flow; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Tests for verifying that a request body publisher supports multiple subscriptions, aka. replayable. + */ +public abstract class ReplayTestSupport { + + @ParameterizedTest + @ValueSource(strings = { + // 2 subscriptions + "subscribe-cancel-subscribe-cancel", + "subscribe-cancel-subscribe-request", + "subscribe-request-subscribe-cancel", + "subscribe-request-subscribe-request", + // 3 subscriptions + "subscribe-cancel-subscribe-cancel-subscribe-request", + "subscribe-cancel-subscribe-request-subscribe-cancel", + "subscribe-cancel-subscribe-request-subscribe-request", + "subscribe-request-subscribe-cancel-subscribe-cancel", + "subscribe-request-subscribe-cancel-subscribe-request", + "subscribe-request-subscribe-request-subscribe-cancel", + "subscribe-request-subscribe-request-subscribe-request", + }) + void testReplay(String opSequence) throws Exception { + for (ReplayTarget replayTarget : createReplayTargets()) { + try (replayTarget) { + System.err.printf("Executing test for replay target: %s%n", replayTarget); + testReplay(opSequence, replayTarget); + } + } + } + + private static void testReplay(String opSequence, ReplayTarget replayTarget) throws InterruptedException { + + // Create the publisher + ByteBuffer expectedBuffer = replayTarget.expectedBuffer; + BodyPublisher publisher = replayTarget.publisher; + assertEquals(replayTarget.expectedContentLength, publisher.contentLength()); + + // Execute the specified operations + RecordingSubscriber subscriber = null; + Flow.Subscription subscription = null; + String[] ops = opSequence.split("-"); + for (int opIndex = 0; opIndex < ops.length; opIndex++) { + String op = ops[opIndex]; + System.err.printf("Executing operation at index %s: %s%n", opIndex, op); + switch (op) { + + case "subscribe": { + subscriber = new RecordingSubscriber(); + publisher.subscribe(subscriber); + assertEquals("onSubscribe", subscriber.invocations.take()); + subscription = (Flow.Subscription) subscriber.invocations.take(); + break; + } + + case "request": { + assert subscription != null; + subscription.request(Long.MAX_VALUE); + if (expectedBuffer.hasRemaining()) { + assertEquals("onNext", subscriber.invocations.take()); + ByteBuffer actualBuffer = (ByteBuffer) subscriber.invocations.take(); + ByteBufferUtils.assertEquals(expectedBuffer, actualBuffer, null); + } + assertEquals("onComplete", subscriber.invocations.take()); + break; + } + + case "cancel": { + assert subscription != null; + subscription.cancel(); + break; + } + + default: throw new IllegalArgumentException("Unknown operation: " + op); + + } + } + + } + + abstract Iterable createReplayTargets(); + + public record ReplayTarget( + ByteBuffer expectedBuffer, + int expectedContentLength, + BodyPublisher publisher, + AutoCloseable resource) + implements AutoCloseable { + + public ReplayTarget(ByteBuffer expectedBuffer, BodyPublisher publisher) { + this(expectedBuffer, expectedBuffer.limit(), publisher, null); + } + + @Override + public void close() throws Exception { + if (resource != null) { + resource.close(); + } + } + + } + +} diff --git a/test/jdk/java/net/httpclient/UserAuthWithAuthenticator.java b/test/jdk/java/net/httpclient/UserAuthWithAuthenticator.java index 90df367a619..6062fcd9448 100644 --- a/test/jdk/java/net/httpclient/UserAuthWithAuthenticator.java +++ b/test/jdk/java/net/httpclient/UserAuthWithAuthenticator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -21,17 +21,33 @@ * questions. */ -import java.io.*; -import java.net.*; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Authenticator; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.PasswordAuthentication; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.URI; +import java.net.URISyntaxException; import java.net.http.HttpClient; +import java.net.http.HttpClient.Version; import java.net.http.HttpRequest; -import java.net.http.HttpOption.Http3DiscoveryMode; import java.net.http.HttpResponse; -import javax.net.ssl.*; +import java.util.LinkedList; +import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.regex.*; -import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.net.ssl.SSLContext; + import jdk.test.lib.net.SimpleSSLContext; import jdk.test.lib.net.URIBuilder; import jdk.httpclient.test.lib.common.HttpServerAdapters; @@ -42,6 +58,8 @@ import jdk.httpclient.test.lib.http2.Http2TestServer; import com.sun.net.httpserver.BasicAuthenticator; import org.junit.jupiter.api.Test; +import static java.net.http.HttpOption.H3_DISCOVERY; +import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.nio.charset.StandardCharsets.US_ASCII; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -119,7 +137,7 @@ class UserAuthWithAuthenticator { private static void h3Test(final boolean useHeader, boolean rightPassword) throws Exception { SSLContext sslContext = SimpleSSLContext.findSSLContext(); try (ExecutorService executor = Executors.newCachedThreadPool(); - HttpTestServer server = HttpTestServer.create(Http3DiscoveryMode.HTTP_3_URI_ONLY, sslContext, executor); + HttpTestServer server = HttpTestServer.create(HTTP_3_URI_ONLY, sslContext, executor); HttpClient client = HttpServerAdapters.createClientBuilderForH3() .sslContext(sslContext) .executor(executor) @@ -164,11 +182,18 @@ class UserAuthWithAuthenticator { .build(); var authHeaderValue = authHeaderValue("user", rightPassword ? "pwd" : "wrongPwd"); - HttpRequest req = HttpRequest.newBuilder(uri) + HttpRequest.Builder reqBuilder = HttpRequest.newBuilder(uri) .version(version) .header(useHeader ? "Authorization" : "X-Ignore", authHeaderValue) - .GET() - .build(); + .GET(); + if (version == Version.HTTP_3) { + // we should not attempt to default to TCP since our server is HTTP_3_URI_ONLY + // setting the option on the request also has the effect of telling + // the client that it can use the full HTTP/3 timeout, since the server + // is known to support HTTP/3 + reqBuilder.setOption(H3_DISCOVERY, HTTP_3_URI_ONLY); + } + HttpRequest req = reqBuilder.build(); HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString()); var sa = (ServerAuth) client.authenticator().orElseThrow(); diff --git a/test/jdk/java/net/httpclient/http3/H3ConnectionPoolTest.java b/test/jdk/java/net/httpclient/http3/H3ConnectionPoolTest.java index 614d564005b..12956604484 100644 --- a/test/jdk/java/net/httpclient/http3/H3ConnectionPoolTest.java +++ b/test/jdk/java/net/httpclient/http3/H3ConnectionPoolTest.java @@ -25,11 +25,13 @@ * @test * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer - * jdk.test.lib.Asserts * jdk.test.lib.Utils * jdk.test.lib.net.SimpleSSLContext * @run junit/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors,http3,quic:hs * -Djdk.internal.httpclient.debug=false + * -Djdk.httpclient.keepalive.timeout.h3=480 + * -Djdk.httpclient.quic.idleTimeout=480 + * -Djdk.test.server.quic.idleTimeout=480 * H3ConnectionPoolTest */ @@ -44,7 +46,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import java.util.function.Supplier; import javax.net.ssl.SSLContext; @@ -60,11 +61,11 @@ import static java.net.http.HttpOption.H3_DISCOVERY; import static java.net.http.HttpOption.Http3DiscoveryMode.ALT_SVC; import static java.net.http.HttpOption.Http3DiscoveryMode.ANY; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; -import static jdk.test.lib.Asserts.assertEquals; -import static jdk.test.lib.Asserts.assertNotEquals; -import static jdk.test.lib.Asserts.assertTrue; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class H3ConnectionPoolTest implements HttpServerAdapters { @@ -257,7 +258,7 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { HttpResponse response2 = client.send(request2, BodyHandlers.ofString()); assertEquals(HTTP_3, response2.version()); checkStatus(200, response2.statusCode()); - assertNotEquals(response2.connectionLabel().get(), response1.connectionLabel().get()); + assertNotEquals(response1.connectionLabel().get(), response2.connectionLabel().get()); // second request with HTTP3_URI_ONLY should reuse a created connection // It should reuse the advertised connection (from response2) if same @@ -295,8 +296,8 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { HttpResponse response2 = client.send(request2, BodyHandlers.ofString()); assertEquals(HTTP_3, response2.version()); checkStatus(200, response2.statusCode()); - assertNotEquals(response2.connectionLabel().get(), h2resp2.connectionLabel().get()); - assertNotEquals(response2.connectionLabel().get(), response1.connectionLabel().get()); + assertNotEquals(h2resp2.connectionLabel().get(), response2.connectionLabel().get()); + assertNotEquals(response1.connectionLabel().get(), response2.connectionLabel().get()); // third request with ALT_SVC should reuse the same advertised // connection (from response2), regardless of same origin... @@ -304,16 +305,16 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { HttpResponse response3 = client.send(request3, BodyHandlers.ofString()); assertEquals(HTTP_3, response3.version()); checkStatus(200, response3.statusCode()); - assertEquals(response3.connectionLabel().get(), response2.connectionLabel().get()); - assertNotEquals(response3.connectionLabel().get(), response1.connectionLabel().get()); + assertEquals(response2.connectionLabel().get(), response3.connectionLabel().get()); + assertNotEquals(response1.connectionLabel().get(), response3.connectionLabel().get()); // fourth request with HTTP_3_URI_ONLY should reuse the first connection, // and not reuse the second. HttpRequest request4 = req1Builder.copy().build(); HttpResponse response4 = client.send(request4, BodyHandlers.ofString()); assertEquals(HTTP_3, response4.version()); - assertEquals(response4.connectionLabel().get(), response1.connectionLabel().get()); - assertNotEquals(response4.connectionLabel().get(), response3.connectionLabel().get()); + assertEquals(response1.connectionLabel().get(), response4.connectionLabel().get()); + assertNotEquals(response3.connectionLabel().get(), response4.connectionLabel().get()); checkStatus(200, response1.statusCode()); } else { System.out.println("WARNING: Couldn't create HTTP/3 server on same port! Can't test all..."); @@ -329,7 +330,7 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { HttpResponse response2 = client.send(request2, BodyHandlers.ofString()); assertEquals(HTTP_3, response2.version()); checkStatus(200, response2.statusCode()); - assertNotEquals(response2.connectionLabel().get(), h2resp2.connectionLabel().get()); + assertNotEquals(h2resp2.connectionLabel().get(), response2.connectionLabel().get()); // third request with ALT_SVC should reuse the same advertised // connection (from response2), regardless of same origin... @@ -337,7 +338,7 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { HttpResponse response3 = client.send(request3, BodyHandlers.ofString()); assertEquals(HTTP_3, response3.version()); checkStatus(200, response3.statusCode()); - assertEquals(response3.connectionLabel().get(), response2.connectionLabel().get()); + assertEquals(response2.connectionLabel().get(), response3.connectionLabel().get()); } } finally { http3OnlyServer.stop(); @@ -417,7 +418,7 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { response2); assertEquals(HTTP_3, response2.version()); checkStatus(200, response2.statusCode()); - assertNotEquals(response2.connectionLabel().get(), c1Label); + assertNotEquals(c1Label, response2.connectionLabel().get()); if (i == 0) { c2Label = response2.connectionLabel().get(); } @@ -494,8 +495,8 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { if (i == 0) { c2Label = response2.connectionLabel().get(); } - assertNotEquals(response2.connectionLabel().get(), h2resp2.connectionLabel().get()); - assertNotEquals(response2.connectionLabel().get(), c1Label); + assertNotEquals(h2resp2.connectionLabel().get(), response2.connectionLabel().get()); + assertNotEquals(c1Label, response2.connectionLabel().get()); assertEquals(c2Label, response2.connectionLabel().orElse(null)); } var expectedLabels = Set.of(c1Label, c2Label); @@ -507,7 +508,7 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { response3); assertEquals(HTTP_3, response3.version()); checkStatus(200, response3.statusCode()); - assertNotEquals(response3.connectionLabel().get(), h2resp2.connectionLabel().get()); + assertNotEquals(h2resp2.connectionLabel().get(), response3.connectionLabel().get()); var label = response3.connectionLabel().orElse(""); assertTrue(expectedLabels.contains(label), "Unexpected label: %s not in %s" .formatted(label, expectedLabels)); @@ -526,7 +527,7 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { HttpResponse response2 = client.send(request2, BodyHandlers.ofString()); assertEquals(HTTP_3, response2.version()); checkStatus(200, response2.statusCode()); - assertNotEquals(response2.connectionLabel().get(), h2resp2.connectionLabel().get()); + assertNotEquals(h2resp2.connectionLabel().get(), response2.connectionLabel().get()); // third request with ALT_SVC should reuse the same advertised // connection (from response2), regardless of same origin... @@ -560,21 +561,4 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { } } - static void checkStrings(String expected, String found) throws Exception { - if (!expected.equals(found)) { - System.err.printf("Test failed: wrong string %s/%s\n", - expected, found); - throw new RuntimeException("Test failed"); - } - } - - - static T logExceptionally(String desc, Throwable t) { - System.out.println(desc + " failed: " + t); - System.err.println(desc + " failed: " + t); - if (t instanceof RuntimeException r) throw r; - if (t instanceof Error e) throw e; - throw new CompletionException(t); - } - } diff --git a/test/jdk/java/nio/Buffer/BulkPutBuffer.java b/test/jdk/java/nio/Buffer/BulkPutBuffer.java index d1d10ebff87..85ebb624d0f 100644 --- a/test/jdk/java/nio/Buffer/BulkPutBuffer.java +++ b/test/jdk/java/nio/Buffer/BulkPutBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -41,18 +41,22 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.stream.Stream; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @bug 8219014 8245121 * @summary Ensure that a bulk put of a buffer into another is correct. * @compile BulkPutBuffer.java - * @run testng/othervm BulkPutBuffer + * @run junit/othervm BulkPutBuffer */ public class BulkPutBuffer { static final long SEED = System.nanoTime(); @@ -195,95 +199,64 @@ public class BulkPutBuffer { nextType = lookup.findVirtual(MyRandom.class, "next" + name, MethodType.methodType(elementType)); } catch (IllegalAccessException | NoSuchMethodException e) { - throw new AssertionError(e); + throw new RuntimeException(e); } } Buffer create(int capacity) throws Throwable { - Class bufferType = typeToAttr.get(elementType).type; + if (bufferType == ByteBuffer.class || kind == BufferKind.DIRECT || + kind == BufferKind.HEAP_VIEW) { - try { - if (bufferType == ByteBuffer.class || - kind == BufferKind.DIRECT || kind == BufferKind.HEAP_VIEW) { - int len = capacity*typeToAttr.get(elementType).bytes; - ByteBuffer bb = (ByteBuffer)allocBB.invoke(len); - byte[] bytes = new byte[len]; - RND.nextBytes(bytes); - bb.put(0, bytes); - if (bufferType == ByteBuffer.class) { - return (Buffer)bb; - } else { - bb.order(order); - return (Buffer)asTypeBuffer.invoke(bb); - } - } else if (bufferType == CharBuffer.class && - kind == BufferKind.STRING) { - char[] array = new char[capacity]; - for (int i = 0; i < capacity; i++) { - array[i] = RND.nextChar(); - } - return CharBuffer.wrap(new String(array)); + int len = capacity*typeToAttr.get(elementType).bytes; + ByteBuffer bb = (ByteBuffer)allocBB.invoke(len); + byte[] bytes = new byte[len]; + RND.nextBytes(bytes); + bb.put(0, bytes); + if (bufferType == ByteBuffer.class) { + return (Buffer)bb; } else { - Buffer buf = (Buffer)alloc.invoke(capacity); - for (int i = 0; i < capacity; i++) { - putAbs.invoke(buf, i, nextType.invoke(RND)); - } - return buf; + bb.order(order); + return (Buffer)asTypeBuffer.invoke(bb); } - } catch (Exception e) { - throw new AssertionError(e); + } else if (bufferType == CharBuffer.class && + kind == BufferKind.STRING) { + char[] array = new char[capacity]; + for (int i = 0; i < capacity; i++) { + array[i] = RND.nextChar(); + } + return CharBuffer.wrap(new String(array)); + } else { + Buffer buf = (Buffer)alloc.invoke(capacity); + for (int i = 0; i < capacity; i++) { + putAbs.invoke(buf, i, nextType.invoke(RND)); + } + return buf; } } void copy(Buffer src, int srcOff, Buffer dst, int dstOff, int length) throws Throwable { - try { - for (int i = 0; i < length; i++) { - putAbs.invoke(dst, dstOff + i, getAbs.invoke(src, srcOff + i)); - } - } catch (ReadOnlyBufferException ro) { - throw ro; - } catch (Exception e) { - throw new AssertionError(e); + for (int i = 0; i < length; i++) { + putAbs.invoke(dst, dstOff + i, getAbs.invoke(src, srcOff + i)); } } Buffer asReadOnlyBuffer(Buffer buf) throws Throwable { - try { - return (Buffer)asReadOnlyBuffer.invoke(buf); - } catch (Exception e) { - throw new AssertionError(e); - } + return (Buffer)asReadOnlyBuffer.invoke(buf); } void put(Buffer src, int srcOff, Buffer dst, int dstOff, int length) throws Throwable { - try { - putBufAbs.invoke(dst, dstOff, src, srcOff, length); - } catch (ReadOnlyBufferException ro) { - throw ro; - } catch (Exception e) { - throw new AssertionError(e); - } + putBufAbs.invoke(dst, dstOff, src, srcOff, length); } void put(Buffer src, Buffer dst) throws Throwable { - try { - putBufRel.invoke(dst, src); - } catch (ReadOnlyBufferException ro) { - throw ro; - } catch (Exception e) { - throw new AssertionError(e); - } + putBufRel.invoke(dst, src); } boolean equals(Buffer src, Buffer dst) throws Throwable { - try { - return Boolean.class.cast(equals.invoke(dst, src)); - } catch (Exception e) { - throw new AssertionError(e); - } + return Boolean.class.cast(equals.invoke(dst, src)); } } @@ -297,68 +270,58 @@ public class BulkPutBuffer { return proxies; } - @DataProvider - static Object[][] proxies() { - ArrayList args = new ArrayList<>(); + static Stream proxies() { + List args = new ArrayList(); for (Class type : typeToAttr.keySet()) { + List proxies = getProxies(type); for (BufferProxy proxy : proxies) { - args.add(new Object[] {proxy}); + args.add(proxy); } } - return args.toArray(Object[][]::new); + return args.stream(); } - @DataProvider - static Object[][] proxyPairs() { - List args = new ArrayList<>(); + static Stream proxyPairs() { + List args = new ArrayList(); for (Class type : typeToAttr.keySet()) { List proxies = getProxies(type); for (BufferProxy proxy1 : proxies) { for (BufferProxy proxy2 : proxies) { - args.add(new Object[] {proxy1, proxy2}); + args.add(Arguments.of(proxy1, proxy2)); } } } - return args.toArray(Object[][]::new); + return args.stream(); } - private static void expectThrows(Class exClass, Assert.ThrowingRunnable r) { - try { - r.run(); - } catch(Throwable e) { - if (e.getClass() != exClass && e.getCause().getClass() != exClass) { - throw new RuntimeException("Expected " + exClass + - "; got " + e.getCause().getClass(), e); - } - } - } - - @Test(dataProvider = "proxies") - public static void testExceptions(BufferProxy bp) throws Throwable { + @ParameterizedTest + @MethodSource("proxies") + public void testExceptions(BufferProxy bp) throws Throwable { int cap = 27; Buffer buf = bp.create(cap); - expectThrows(IndexOutOfBoundsException.class, + assertThrows(IndexOutOfBoundsException.class, () -> bp.put(buf, -1, buf, 0, 1)); - expectThrows(IndexOutOfBoundsException.class, + assertThrows(IndexOutOfBoundsException.class, () -> bp.put(buf, 0, buf, -1, 1)); - expectThrows(IndexOutOfBoundsException.class, + assertThrows(IndexOutOfBoundsException.class, () -> bp.put(buf, 1, buf, 0, cap)); - expectThrows(IndexOutOfBoundsException.class, + assertThrows(IndexOutOfBoundsException.class, () -> bp.put(buf, 0, buf, 1, cap)); - expectThrows(IndexOutOfBoundsException.class, + assertThrows(IndexOutOfBoundsException.class, () -> bp.put(buf, 0, buf, 0, cap + 1)); - expectThrows(IndexOutOfBoundsException.class, + assertThrows(IndexOutOfBoundsException.class, () -> bp.put(buf, 0, buf, 0, Integer.MAX_VALUE)); Buffer rob = buf.isReadOnly() ? buf : bp.asReadOnlyBuffer(buf); - expectThrows(ReadOnlyBufferException.class, + assertThrows(ReadOnlyBufferException.class, () -> bp.put(buf, 0, rob, 0, cap)); } - @Test(dataProvider = "proxies") - public static void testSelf(BufferProxy bp) throws Throwable { + @ParameterizedTest + @MethodSource("proxies") + public void testSelf(BufferProxy bp) throws Throwable { for (int i = 0; i < ITERATIONS; i++) { int cap = RND.nextInt(MAX_CAPACITY); Buffer buf = bp.create(cap); @@ -371,7 +334,7 @@ public class BulkPutBuffer { Buffer lowerCopy = bp.create(lowerLength); if (lowerCopy.isReadOnly()) { - Assert.expectThrows(ReadOnlyBufferException.class, + assertThrows(ReadOnlyBufferException.class, () -> bp.copy(lower, 0, lowerCopy, 0, lowerLength)); break; } @@ -385,7 +348,7 @@ public class BulkPutBuffer { bp.put(lower, middle); middle.flip(); - Assert.assertTrue(bp.equals(lowerCopy, middle), + assertTrue(bp.equals(lowerCopy, middle), String.format("%d %s %d %d %d %d%n", SEED, buf.getClass().getName(), cap, lowerOffset, lowerLength, middleOffset)); @@ -395,15 +358,16 @@ public class BulkPutBuffer { bp.put(buf, lowerOffset, buf, middleOffset, lowerLength); - Assert.assertTrue(bp.equals(lowerCopy, middle), + assertTrue(bp.equals(lowerCopy, middle), String.format("%d %s %d %d %d %d%n", SEED, buf.getClass().getName(), cap, lowerOffset, lowerLength, middleOffset)); } } - @Test(dataProvider = "proxyPairs") - public static void testPairs(BufferProxy bp, BufferProxy sbp) throws Throwable { + @ParameterizedTest + @MethodSource("proxyPairs") + public void testPairs(BufferProxy bp, BufferProxy sbp) throws Throwable { for (int i = 0; i < ITERATIONS; i++) { int cap = Math.max(4, RND.nextInt(MAX_CAPACITY)); int cap2 = cap/2; @@ -426,7 +390,7 @@ public class BulkPutBuffer { src.limit(slim); if (buf.isReadOnly()) { - Assert.expectThrows(ReadOnlyBufferException.class, + assertThrows(ReadOnlyBufferException.class, () -> bp.put(src, buf)); break; } @@ -438,7 +402,7 @@ public class BulkPutBuffer { buf.reset(); src.reset(); - Assert.assertTrue(bp.equals(src, buf), + assertTrue(bp.equals(src, buf), String.format("%d %s %d %d %d %s %d %d %d%n", SEED, buf.getClass().getName(), cap, pos, lim, src.getClass().getName(), scap, spos, slim)); @@ -452,7 +416,7 @@ public class BulkPutBuffer { buf.position(pos); buf.limit(lim); - Assert.assertTrue(bp.equals(src, buf), + assertTrue(bp.equals(src, buf), String.format("%d %s %d %d %d %s %d %d %d%n", SEED, buf.getClass().getName(), cap, pos, lim, src.getClass().getName(), scap, spos, slim)); diff --git a/test/jdk/java/nio/Buffer/ByteBufferViews.java b/test/jdk/java/nio/Buffer/ByteBufferViews.java index f0136939d53..17bd6af4033 100644 --- a/test/jdk/java/nio/Buffer/ByteBufferViews.java +++ b/test/jdk/java/nio/Buffer/ByteBufferViews.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -24,12 +24,9 @@ /* @test * @summary Binary data and view tests for byte buffers * @bug 8159257 8258955 - * @run testng ByteBufferViews + * @run junit ByteBufferViews */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -46,8 +43,15 @@ import java.util.function.IntFunction; import java.util.function.IntUnaryOperator; import java.util.function.UnaryOperator; import java.util.stream.Collectors; +import java.util.stream.Stream; -import static org.testng.Assert.*; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.fail; public class ByteBufferViews { static final int SIZE = 32; @@ -127,14 +131,15 @@ public class ByteBufferViews { // Creates a cross product of test arguments for // buffer allocator functions and buffer view functions - static Object[][] product(List> la, - List> lb) { + static Stream product(List> la, + List> lb) { return la.stream().flatMap(lae -> lb.stream(). - map(lbe -> List.of( - lae.getKey() + " -> " + lbe.getKey(), - lae.getValue(), - lbe.getValue()).toArray() - )).toArray(Object[][]::new); + map(lbe -> Arguments.of + ( + lae.getKey() + " -> " + lbe.getKey(), + lae.getValue(), + lbe.getValue()) + )); } static void assertValues(int i, Object bValue, Object bbValue, ByteBuffer bb) { @@ -167,8 +172,7 @@ public class ByteBufferViews { } - @DataProvider - public static Object[][] shortViewProvider() { + public static Stream shortBufferViews() { List>> bfs = List.of( Map.entry("bb.asShortBuffer()", bb -> bb.asShortBuffer()), @@ -186,7 +190,8 @@ public class ByteBufferViews { return product(BYTE_BUFFER_FUNCTIONS, bfs); } - @Test(dataProvider = "shortViewProvider") + @ParameterizedTest + @MethodSource("shortBufferViews") public void testShortGet(String desc, IntFunction fbb, Function fbi) { ByteBuffer bb = allocate(fbb); @@ -213,7 +218,8 @@ public class ByteBufferViews { } - @Test(dataProvider = "shortViewProvider") + @ParameterizedTest + @MethodSource("shortBufferViews") public void testShortPut(String desc, IntFunction fbb, Function fbi) { ByteBuffer bbfilled = allocate(fbb); @@ -271,8 +277,7 @@ public class ByteBufferViews { } } - @DataProvider - public static Object[][] charViewProvider() { + public static Stream charBufferViews() { List>> bfs = List.of( Map.entry("bb.asCharBuffer()", bb -> bb.asCharBuffer()), @@ -290,7 +295,8 @@ public class ByteBufferViews { return product(BYTE_BUFFER_FUNCTIONS, bfs); } - @Test(dataProvider = "charViewProvider") + @ParameterizedTest + @MethodSource("charBufferViews") public void testCharGet(String desc, IntFunction fbb, Function fbi) { ByteBuffer bb = allocate(fbb); @@ -317,7 +323,8 @@ public class ByteBufferViews { } - @Test(dataProvider = "charViewProvider") + @ParameterizedTest + @MethodSource("charBufferViews") public void testCharPut(String desc, IntFunction fbb, Function fbi) { ByteBuffer bbfilled = allocate(fbb); @@ -368,8 +375,7 @@ public class ByteBufferViews { } - @DataProvider - public static Object[][] intViewProvider() { + public static Stream intBufferViews() { List>> bfs = List.of( Map.entry("bb.asIntBuffer()", bb -> bb.asIntBuffer()), @@ -387,7 +393,8 @@ public class ByteBufferViews { return product(BYTE_BUFFER_FUNCTIONS, bfs); } - @Test(dataProvider = "intViewProvider") + @ParameterizedTest + @MethodSource("intBufferViews") public void testIntGet(String desc, IntFunction fbb, Function fbi) { ByteBuffer bb = allocate(fbb); @@ -414,7 +421,8 @@ public class ByteBufferViews { } - @Test(dataProvider = "intViewProvider") + @ParameterizedTest + @MethodSource("intBufferViews") public void testIntPut(String desc, IntFunction fbb, Function fbi) { ByteBuffer bbfilled = allocate(fbb); @@ -475,8 +483,7 @@ public class ByteBufferViews { } - @DataProvider - public static Object[][] longViewProvider() { + public static Stream longBufferViews() { List>> bfs = List.of( Map.entry("bb.asLongBuffer()", bb -> bb.asLongBuffer()), @@ -494,7 +501,8 @@ public class ByteBufferViews { return product(BYTE_BUFFER_FUNCTIONS, bfs); } - @Test(dataProvider = "longViewProvider") + @ParameterizedTest + @MethodSource("longBufferViews") public void testLongGet(String desc, IntFunction fbb, Function fbi) { ByteBuffer bb = allocate(fbb); @@ -521,7 +529,8 @@ public class ByteBufferViews { } - @Test(dataProvider = "longViewProvider") + @ParameterizedTest + @MethodSource("longBufferViews") public void testLongPut(String desc, IntFunction fbb, Function fbi) { ByteBuffer bbfilled = allocate(fbb); @@ -587,9 +596,7 @@ public class ByteBufferViews { } } - - @DataProvider - public static Object[][] floatViewProvider() { + public static Stream floatBufferViews() { List>> bfs = List.of( Map.entry("bb.asFloatBuffer()", bb -> bb.asFloatBuffer()), @@ -607,7 +614,8 @@ public class ByteBufferViews { return product(BYTE_BUFFER_FUNCTIONS, bfs); } - @Test(dataProvider = "floatViewProvider") + @ParameterizedTest + @MethodSource("floatBufferViews") public void testFloatGet(String desc, IntFunction fbb, Function fbi) { ByteBuffer bb = allocate(fbb); @@ -634,7 +642,8 @@ public class ByteBufferViews { } - @Test(dataProvider = "floatViewProvider") + @ParameterizedTest + @MethodSource("floatBufferViews") public void testFloatPut(String desc, IntFunction fbb, Function fbi) { ByteBuffer bbfilled = allocate(fbb); @@ -684,10 +693,7 @@ public class ByteBufferViews { return Float.intBitsToFloat(getIntFromBytes(bb, i)); } - - - @DataProvider - public static Object[][] doubleViewProvider() { + public static Stream doubleBufferViews() { List>> bfs = List.of( Map.entry("bb.asDoubleBuffer()", bb -> bb.asDoubleBuffer()), @@ -705,7 +711,7 @@ public class ByteBufferViews { return product(BYTE_BUFFER_FUNCTIONS, bfs); } - @Test(dataProvider = "doubleViewProvider") + @MethodSource("doubleBufferViews") public void testDoubleGet(String desc, IntFunction fbb, Function fbi) { ByteBuffer bb = allocate(fbb); @@ -732,7 +738,8 @@ public class ByteBufferViews { } - @Test(dataProvider = "doubleViewProvider") + @ParameterizedTest + @MethodSource("doubleBufferViews") public void testDoublePut(String desc, IntFunction fbb, Function fbi) { ByteBuffer bbfilled = allocate(fbb); diff --git a/test/jdk/java/nio/Buffer/Chars.java b/test/jdk/java/nio/Buffer/Chars.java index 01b53ea2405..1b978eecb74 100644 --- a/test/jdk/java/nio/Buffer/Chars.java +++ b/test/jdk/java/nio/Buffer/Chars.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, 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 @@ -25,7 +25,7 @@ * @test * @bug 8014854 * @summary Exercises CharBuffer#chars on each of the CharBuffer types - * @run testng Chars + * @run junit Chars * @key randomness */ @@ -35,11 +35,13 @@ import java.nio.CharBuffer; import java.util.ArrayList; import java.util.List; import java.util.Random; +import java.util.stream.Stream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -import static org.testng.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class Chars { @@ -98,8 +100,7 @@ public class Chars { buffers.add(randomizeRange(cb.asReadOnlyBuffer())); } - @DataProvider(name = "charbuffers") - public Object[][] createCharBuffers() { + private static Stream createCharBuffers() { List buffers = new ArrayList<>(); // heap @@ -119,20 +120,21 @@ public class Chars { // read-only buffer backed by a CharSequence buffers.add(CharBuffer.wrap(randomize(CharBuffer.allocate(SIZE)))); - Object[][] params = new Object[buffers.size()][]; + List params = new ArrayList(); for (int i = 0; i < buffers.size(); i++) { CharBuffer cb = buffers.get(i); - params[i] = new Object[] { cb.getClass().getName(), cb }; + params.add((Arguments.of(cb.getClass().getName(), cb))); } - return params; + return params.stream(); } - @Test(dataProvider = "charbuffers") + @ParameterizedTest + @MethodSource("createCharBuffers") public void testChars(String type, CharBuffer cb) { - System.out.format("%s position=%d, limit=%d%n", type, cb.position(), cb.limit()); + System.err.format("%s position=%d, limit=%d%n", type, cb.position(), cb.limit()); int expected = intSum(cb); - assertEquals(cb.chars().sum(), expected); - assertEquals(cb.chars().parallel().sum(), expected); + assertEquals(expected, cb.chars().sum()); + assertEquals(expected, cb.chars().parallel().sum()); } } diff --git a/test/jdk/java/nio/Buffer/EqualsCompareTest.java b/test/jdk/java/nio/Buffer/EqualsCompareTest.java index 03bd7c26a58..8c098767253 100644 --- a/test/jdk/java/nio/Buffer/EqualsCompareTest.java +++ b/test/jdk/java/nio/Buffer/EqualsCompareTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -21,10 +21,6 @@ * questions. */ -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import java.io.IOException; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -48,15 +44,25 @@ import java.util.Set; import java.util.function.BiFunction; import java.util.function.LongFunction; import java.util.stream.IntStream; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.file.StandardOpenOption.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @bug 8193085 8199773 * @summary tests for buffer equals and compare - * @run testng EqualsCompareTest + * @run junit EqualsCompareTest */ public class EqualsCompareTest { @@ -446,89 +452,66 @@ public class EqualsCompareTest { } } - static Object[][] bufferTypes; - - @DataProvider - public static Object[][] bufferTypesProvider() { - if (bufferTypes == null) { - bufferTypes = new Object[][]{ - {new BufferType.Bytes(BufferKind.HEAP)}, - {new BufferType.Bytes(BufferKind.DIRECT)}, - {new BufferType.Chars(BufferKind.HEAP)}, - {new BufferType.Chars(BufferKind.HEAP_VIEW)}, - {new BufferType.Chars(BufferKind.DIRECT)}, - {new BufferType.Shorts(BufferKind.HEAP)}, - {new BufferType.Shorts(BufferKind.HEAP_VIEW)}, - {new BufferType.Shorts(BufferKind.DIRECT)}, - {new BufferType.Ints(BufferKind.HEAP)}, - {new BufferType.Ints(BufferKind.HEAP_VIEW)}, - {new BufferType.Ints(BufferKind.DIRECT)}, - {new BufferType.Floats(BufferKind.HEAP)}, - {new BufferType.Floats(BufferKind.HEAP_VIEW)}, - {new BufferType.Floats(BufferKind.DIRECT)}, - {new BufferType.Longs(BufferKind.HEAP)}, - {new BufferType.Longs(BufferKind.HEAP_VIEW)}, - {new BufferType.Longs(BufferKind.DIRECT)}, - {new BufferType.Doubles(BufferKind.HEAP)}, - {new BufferType.Doubles(BufferKind.HEAP_VIEW)}, - {new BufferType.Doubles(BufferKind.DIRECT)}, - }; - } - return bufferTypes; + public static Stream bufferTypesSource() { + return Stream.of + (new BufferType.Bytes(BufferKind.HEAP), + new BufferType.Bytes(BufferKind.DIRECT), + new BufferType.Chars(BufferKind.HEAP), + new BufferType.Chars(BufferKind.HEAP_VIEW), + new BufferType.Chars(BufferKind.DIRECT), + new BufferType.Shorts(BufferKind.HEAP), + new BufferType.Shorts(BufferKind.HEAP_VIEW), + new BufferType.Shorts(BufferKind.DIRECT), + new BufferType.Ints(BufferKind.HEAP), + new BufferType.Ints(BufferKind.HEAP_VIEW), + new BufferType.Ints(BufferKind.DIRECT), + new BufferType.Floats(BufferKind.HEAP), + new BufferType.Floats(BufferKind.HEAP_VIEW), + new BufferType.Floats(BufferKind.DIRECT), + new BufferType.Longs(BufferKind.HEAP), + new BufferType.Longs(BufferKind.HEAP_VIEW), + new BufferType.Longs(BufferKind.DIRECT), + new BufferType.Doubles(BufferKind.HEAP), + new BufferType.Doubles(BufferKind.HEAP_VIEW), + new BufferType.Doubles(BufferKind.DIRECT)); } - - static Object[][] floatbufferTypes; - - @DataProvider - public static Object[][] floatBufferTypesProvider() { - if (floatbufferTypes == null) { + public static Stream floatBufferTypesSource() { LongFunction bTof = rb -> Float.intBitsToFloat((int) rb); LongFunction bToD = Double::longBitsToDouble; - floatbufferTypes = new Object[][]{ - // canonical and non-canonical NaNs - // If conversion is a signalling NaN it may be subject to conversion to a - // quiet NaN on some processors, even if a copy is performed - // The tests assume that if conversion occurs it does not convert to the - // canonical NaN - new Object[]{new BufferType.Floats(BufferKind.HEAP), 0x7fc00000L, 0x7f800001L, bTof}, - new Object[]{new BufferType.Floats(BufferKind.HEAP_VIEW), 0x7fc00000L, 0x7f800001L, bTof}, - new Object[]{new BufferType.Floats(BufferKind.DIRECT), 0x7fc00000L, 0x7f800001L, bTof}, - new Object[]{new BufferType.Doubles(BufferKind.HEAP), 0x7ff8000000000000L, 0x7ff0000000000001L, bToD}, - new Object[]{new BufferType.Doubles(BufferKind.HEAP_VIEW), 0x7ff8000000000000L, 0x7ff0000000000001L, bToD}, - new Object[]{new BufferType.Doubles(BufferKind.DIRECT), 0x7ff8000000000000L, 0x7ff0000000000001L, bToD}, + return Stream.of + (// canonical and non-canonical NaNs + // If conversion is a signalling NaN it may be subject to conversion to a + // quiet NaN on some processors, even if a copy is performed + // The tests assume that if conversion occurs it does not convert to the + // canonical NaN + Arguments.of(new BufferType.Floats(BufferKind.HEAP), 0x7fc00000L, 0x7f800001L, bTof), + Arguments.of(new BufferType.Floats(BufferKind.HEAP_VIEW), 0x7fc00000L, 0x7f800001L, bTof), + Arguments.of(new BufferType.Floats(BufferKind.DIRECT), 0x7fc00000L, 0x7f800001L, bTof), + Arguments.of(new BufferType.Doubles(BufferKind.HEAP), 0x7ff8000000000000L, 0x7ff0000000000001L, bToD), + Arguments.of(new BufferType.Doubles(BufferKind.HEAP_VIEW), 0x7ff8000000000000L, 0x7ff0000000000001L, bToD), + Arguments.of(new BufferType.Doubles(BufferKind.DIRECT), 0x7ff8000000000000L, 0x7ff0000000000001L, bToD), - // +0.0 and -0.0 - new Object[]{new BufferType.Floats(BufferKind.HEAP), 0x0L, 0x80000000L, bTof}, - new Object[]{new BufferType.Floats(BufferKind.HEAP_VIEW), 0x0L, 0x80000000L, bTof}, - new Object[]{new BufferType.Floats(BufferKind.DIRECT), 0x0L, 0x80000000L, bTof}, - new Object[]{new BufferType.Doubles(BufferKind.HEAP), 0x0L, 0x8000000000000000L, bToD}, - new Object[]{new BufferType.Doubles(BufferKind.HEAP_VIEW), 0x0L, 0x8000000000000000L, bToD}, - new Object[]{new BufferType.Doubles(BufferKind.DIRECT), 0x0L, 0x8000000000000000L, bToD}, - }; - } - return floatbufferTypes; + // +0.0 and -0.0 + Arguments.of(new BufferType.Floats(BufferKind.HEAP), 0x0L, 0x80000000L, bTof), + Arguments.of(new BufferType.Floats(BufferKind.HEAP_VIEW), 0x0L, 0x80000000L, bTof), + Arguments.of(new BufferType.Floats(BufferKind.DIRECT), 0x0L, 0x80000000L, bTof), + Arguments.of(new BufferType.Doubles(BufferKind.HEAP), 0x0L, 0x8000000000000000L, bToD), + Arguments.of(new BufferType.Doubles(BufferKind.HEAP_VIEW), 0x0L, 0x8000000000000000L, bToD), + Arguments.of(new BufferType.Doubles(BufferKind.DIRECT), 0x0L, 0x8000000000000000L, bToD)); } - - static Object[][] charBufferTypes; - - @DataProvider - public static Object[][] charBufferTypesProvider() { - if (charBufferTypes == null) { - charBufferTypes = new Object[][]{ - {new BufferType.Chars(BufferKind.HEAP)}, - {new BufferType.Chars(BufferKind.HEAP_VIEW)}, - {new BufferType.Chars(BufferKind.DIRECT)}, - }; - } - return charBufferTypes; + public static Stream charBufferTypesSource() { + return Stream.of + (Arguments.of(new BufferType.Chars(BufferKind.HEAP)), + Arguments.of(new BufferType.Chars(BufferKind.HEAP_VIEW)), + Arguments.of(new BufferType.Chars(BufferKind.DIRECT))); } - // Tests all primitive buffers - @Test(dataProvider = "bufferTypesProvider") + @ParameterizedTest + @MethodSource("bufferTypesSource") void testBuffers(BufferType bufferType) { // Test with buffers of the same byte order (BE) @@ -559,7 +542,8 @@ public class EqualsCompareTest { } // Tests float and double buffers with edge-case values (NaN, -0.0, +0.0) - @Test(dataProvider = "floatBufferTypesProvider") + @ParameterizedTest + @MethodSource("floatBufferTypesSource") public void testFloatBuffers( BufferType bufferType, long rawBitsA, long rawBitsB, @@ -596,17 +580,18 @@ public class EqualsCompareTest { // Sanity check int size = arraySizeFor(bufferType.elementType); - Assert.assertTrue(bufferType.pairWiseEquals(allAs.apply(bufferType, size), - allBs.apply(bufferType, size))); - Assert.assertTrue(bufferType.equals(allAs.apply(bufferType, size), - allBs.apply(bufferType, size))); + assertTrue(bufferType.pairWiseEquals(allAs.apply(bufferType, size), + allBs.apply(bufferType, size))); + assertTrue(bufferType.equals(allAs.apply(bufferType, size), + allBs.apply(bufferType, size))); testBufferType(bufferType, allAs, allBs); testBufferType(bufferType, allAs, halfBs); } // Tests CharBuffer for region sources and CharSequence sources - @Test(dataProvider = "charBufferTypesProvider") + @ParameterizedTest + @MethodSource("charBufferTypesSource") public void testCharBuffers(BufferType.Chars charBufferType) { BiFunction constructor = (at, s) -> { @@ -623,7 +608,6 @@ public class EqualsCompareTest { testBufferType(charBufferType, constructor, constructorX); } - > void testBufferType(BT bt, BiFunction aConstructor, @@ -652,26 +636,26 @@ public class EqualsCompareTest { : b; boolean eq = bt.pairWiseEquals(as, bs); - Assert.assertEquals(bt.equals(as, bs), eq); - Assert.assertEquals(bt.equals(bs, as), eq); + assertEquals(eq, bt.equals(as, bs)); + assertEquals(eq, bt.equals(bs, as)); if (eq) { - Assert.assertEquals(bt.compare(as, bs), 0); - Assert.assertEquals(bt.compare(bs, as), 0); + assertEquals(0, bt.compare(as, bs)); + assertEquals(0, bt.compare(bs, as)); // If buffers are equal, there shall be no mismatch - Assert.assertEquals(bt.mismatch(as, bs), -1); - Assert.assertEquals(bt.mismatch(bs, as), -1); + assertEquals(-1, bt.mismatch(as, bs)); + assertEquals(-1, bt.mismatch(bs, as)); } else { int aCb = bt.compare(as, bs); int bCa = bt.compare(bs, as); int v = Integer.signum(aCb) * Integer.signum(bCa); - Assert.assertTrue(v == -1); + assertEquals(-1, v); int aMs = bt.mismatch(as, bs); int bMs = bt.mismatch(bs, as); - Assert.assertNotEquals(aMs, -1); - Assert.assertEquals(aMs, bMs); + assertNotEquals(-1, aMs); + assertEquals(bMs, aMs); } } } @@ -686,17 +670,17 @@ public class EqualsCompareTest { // Create common prefix with a length of i - aFrom bt.set(c, i, -1); - Assert.assertFalse(bt.equals(c, a)); + assertFalse(bt.equals(c, a)); int cCa = bt.compare(cs, as); int aCc = bt.compare(as, cs); int v = Integer.signum(cCa) * Integer.signum(aCc); - Assert.assertTrue(v == -1); + assertEquals(-1, v); int cMa = bt.mismatch(cs, as); int aMc = bt.mismatch(as, cs); - Assert.assertEquals(cMa, aMc); - Assert.assertEquals(cMa, i - aFrom); + assertEquals(aMc, cMa); + assertEquals(i - aFrom, cMa); } } } @@ -731,8 +715,8 @@ public class EqualsCompareTest { try (FileChannel fc = FileChannel.open(path, READ, DELETE_ON_CLOSE)) { MappedByteBuffer one = fc.map(FileChannel.MapMode.READ_ONLY, 0, bytes.length); ByteBuffer two = ByteBuffer.wrap(bytes); - Assert.assertEquals(one, two); - Assert.assertEquals(one.hashCode(), two.hashCode()); + assertEquals(two, one); + assertEquals(two.hashCode(), one.hashCode()); } } } diff --git a/test/jdk/java/nio/Buffer/ReachabilityTest.java b/test/jdk/java/nio/Buffer/ReachabilityTest.java index 2b0a2710b46..114077b3626 100644 --- a/test/jdk/java/nio/Buffer/ReachabilityTest.java +++ b/test/jdk/java/nio/Buffer/ReachabilityTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -24,12 +24,9 @@ /* @test * @bug 8208362 * @summary Tests reachability from source to dependent direct byte buffers - * @run testng ReachabilityTest + * @run junit ReachabilityTest */ -import org.testng.Assert; -import org.testng.annotations.Test; - import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; @@ -41,8 +38,14 @@ import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.function.UnaryOperator; +import org.junit.jupiter.api.Test; + import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + public class ReachabilityTest { @Test @@ -98,13 +101,13 @@ public class ReachabilityTest { } } } catch (InterruptedException unexpected) { - throw new AssertionError("unexpected InterruptedException"); + fail("unexpected InterruptedException"); } // Some or all of the intermediate values must be GC'ed - Assert.assertTrue(collected); + assertTrue(collected); // The root should never be GC'ed - Assert.assertNotNull(root.get()); + assertNotNull(root.get()); Reference.reachabilityFence(t); } diff --git a/test/jdk/java/nio/MappedByteBuffer/ForceViews.java b/test/jdk/java/nio/MappedByteBuffer/ForceViews.java index 57ddac28cdf..950b35dc0c3 100644 --- a/test/jdk/java/nio/MappedByteBuffer/ForceViews.java +++ b/test/jdk/java/nio/MappedByteBuffer/ForceViews.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -25,46 +25,31 @@ * @test * @bug 4833719 * @summary Verify MappedByteBuffer force on compact, duplicate, and slice views - * @run testng ForceViews + * @run junit ForceViews */ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.ReadOnlyBufferException; import java.nio.channels.FileChannel; -import static java.nio.channels.FileChannel.MapMode.*; import java.nio.file.Path; -import static java.nio.file.StandardOpenOption.*; import java.util.function.BiFunction; +import java.util.stream.Stream; -import org.testng.Assert; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import static java.nio.channels.FileChannel.MapMode.*; +import static java.nio.file.StandardOpenOption.*; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class ForceViews { static record Segment(int position, int length) {} - private FileChannel fc; - - @BeforeTest(alwaysRun=true) - public void openChannel() throws IOException { - Path file = Path.of(".", "junk"); - fc = FileChannel.open(file, CREATE_NEW, READ, WRITE, DELETE_ON_CLOSE); - ByteBuffer buf = ByteBuffer.wrap(new byte[1024]); - fc.write(buf); - fc.position(0); - } - - @AfterTest(alwaysRun=true) - public void closeChannel() throws IOException { - fc.close(); - } - - @DataProvider - public Object[][] provider() throws IOException { + public static Stream provider() throws IOException { BiFunction absSlice = (m, s) -> { return m.slice(s.position, s.length); }; BiFunction relSlice = @@ -75,42 +60,47 @@ public class ForceViews { BiFunction compact = (m, s) -> { return m.compact(); }; - Object[][] result = new Object[][] { - {"Absolute slice", fc, 256, 512, 128, 128, 32, 32, absSlice}, - {"Relative slice", fc, 256, 512, 0, 128, 32, 32, relSlice}, - {"Duplicate", fc, 256, 512, 0, 256, 32, 32, duplicate}, - {"Compact", fc, 256, 512, 0, 256, 32, 32, compact} - }; - - return result; + return Stream.of + (Arguments.of("Absolute slice", 256, 512, 128, 128, 32, 32, absSlice), + Arguments.of("Relative slice", 256, 512, 0, 128, 32, 32, relSlice), + Arguments.of("Duplicate", 256, 512, 0, 256, 32, 32, duplicate), + Arguments.of("Compact", 256, 512, 0, 256, 32, 32, compact)); } - @Test(dataProvider = "provider") - public void test(String tst, FileChannel fc, int mapPosition, int mapLength, - int sliceIndex, int sliceLength, int regionOffset, int regionLength, - BiFunction f) + @ParameterizedTest + @MethodSource("provider") + public void test(String tst, int mapPosition, int mapLength, + int sliceIndex, int sliceLength, int regionOffset, + int regionLength, + BiFunction f) throws Exception { - MappedByteBuffer mbb = fc.map(READ_WRITE, mapPosition, mapLength); - mbb = f.apply(mbb, new Segment(sliceIndex, sliceLength)); - for (int i = regionOffset; i < regionOffset + regionLength; i++) { - mbb.put(i, (byte)i); - } - mbb.force(regionOffset, regionOffset + regionLength); - int fcPos = mapPosition + sliceIndex + regionOffset; - int mbbPos = regionOffset; - int length = regionLength; + Path file = Path.of(".", "junk"); + try (FileChannel fc = FileChannel.open(file, CREATE, READ, WRITE, DELETE_ON_CLOSE)) { + fc.write(ByteBuffer.wrap(new byte[1024])); + fc.position(0); - ByteBuffer buf = ByteBuffer.allocate(length); - fc.position(fcPos); - fc.read(buf); - for (int i = 0; i < length; i++) { - int fcVal = buf.get(i); - int mbbVal = mbb.get(mbbPos + i); - int val = regionOffset + i; - Assert.assertTrue(fcVal == val && mbbVal == val, - String.format("%s: i %d, fcVal %d, mbbVal %d, val %d", - tst, i, fcVal, mbbVal, val)); + MappedByteBuffer mbb = fc.map(READ_WRITE, mapPosition, mapLength); + mbb = f.apply(mbb, new Segment(sliceIndex, sliceLength)); + for (int i = regionOffset; i < regionOffset + regionLength; i++) { + mbb.put(i, (byte)i); + } + mbb.force(regionOffset, regionOffset + regionLength); + + int fcPos = mapPosition + sliceIndex + regionOffset; + int mbbPos = regionOffset; + int length = regionLength; + + ByteBuffer buf = ByteBuffer.allocate(length); + fc.position(fcPos); + fc.read(buf); + for (int i = 0; i < length; i++) { + int fcVal = buf.get(i); + int mbbVal = mbb.get(mbbPos + i); + int val = regionOffset + i; + assertEquals(val, fcVal); + assertEquals(val, mbbVal); + } } } } diff --git a/test/jdk/java/nio/channels/Channels/EncodingTest.java b/test/jdk/java/nio/channels/Channels/EncodingTest.java index 1f2530ae41b..341439524c1 100644 --- a/test/jdk/java/nio/channels/Channels/EncodingTest.java +++ b/test/jdk/java/nio/channels/Channels/EncodingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -34,16 +34,21 @@ import java.nio.charset.Charset; import java.nio.charset.MalformedInputException; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * @test * @bug 8183743 * @summary Test to verify the new overload method with Charset functions the same * as the existing method that takes a charset name. - * @run testng EncodingTest + * @run junit EncodingTest */ public class EncodingTest { static final int ITERATIONS = 2; @@ -73,53 +78,50 @@ public class EncodingTest { } } - String testFile = Paths.get(USER_DIR, "channelsEncodingTest.txt").toString(); - String testIllegalInput = Paths.get(USER_DIR, "channelsIllegalInputTest.txt").toString(); - String testIllegalOutput = Paths.get(USER_DIR, "channelsIllegalOutputTest.txt").toString(); + static String testFile = Paths.get(USER_DIR, "channelsEncodingTest.txt").toString(); + static String testIllegalInput = Paths.get(USER_DIR, "channelsIllegalInputTest.txt").toString(); + static String testIllegalOutput = Paths.get(USER_DIR, "channelsIllegalOutputTest.txt").toString(); /* * DataProvider for read and write test. * Writes and reads with the same encoding */ - @DataProvider(name = "writeAndRead") - public Object[][] getWRParameters() { - return new Object[][]{ - {testFile, StandardCharsets.ISO_8859_1.name(), null, - StandardCharsets.ISO_8859_1.name(), StandardCharsets.ISO_8859_1}, - {testFile, null, StandardCharsets.ISO_8859_1, - StandardCharsets.ISO_8859_1.name(), StandardCharsets.ISO_8859_1}, - {testFile, StandardCharsets.UTF_8.name(), null, - StandardCharsets.UTF_8.name(), StandardCharsets.UTF_8}, - {testFile, null, StandardCharsets.UTF_8, - StandardCharsets.UTF_8.name(), StandardCharsets.UTF_8} - }; + public static Stream writeAndRead() { + return Stream.of + (Arguments.of(testFile, StandardCharsets.ISO_8859_1.name(), null, + StandardCharsets.ISO_8859_1.name(), StandardCharsets.ISO_8859_1), + Arguments.of(testFile, null, StandardCharsets.ISO_8859_1, + StandardCharsets.ISO_8859_1.name(), StandardCharsets.ISO_8859_1), + Arguments.of(testFile, StandardCharsets.UTF_8.name(), null, + StandardCharsets.UTF_8.name(), StandardCharsets.UTF_8), + Arguments.of(testFile, null, StandardCharsets.UTF_8, + StandardCharsets.UTF_8.name(), StandardCharsets.UTF_8) + ); } /* * DataProvider for illegal input test * Writes the data in ISO8859 and reads with UTF8, expects MalformedInputException */ - @DataProvider(name = "illegalInput") - public Object[][] getParameters() { - return new Object[][]{ - {testIllegalInput, StandardCharsets.ISO_8859_1.name(), null, StandardCharsets.UTF_8.name(), null}, - {testIllegalInput, StandardCharsets.ISO_8859_1.name(), null, null, StandardCharsets.UTF_8}, - {testIllegalInput, null, StandardCharsets.ISO_8859_1, StandardCharsets.UTF_8.name(), null}, - {testIllegalInput, null, StandardCharsets.ISO_8859_1, null, StandardCharsets.UTF_8}, - }; + public static Stream illegalInput() { + return Stream.of + (Arguments.of(testIllegalInput, StandardCharsets.ISO_8859_1.name(), null, StandardCharsets.UTF_8.name(), null), + Arguments.of(testIllegalInput, StandardCharsets.ISO_8859_1.name(), null, null, StandardCharsets.UTF_8), + Arguments.of(testIllegalInput, null, StandardCharsets.ISO_8859_1, StandardCharsets.UTF_8.name(), null), + Arguments.of(testIllegalInput, null, StandardCharsets.ISO_8859_1, null, StandardCharsets.UTF_8) + ); } /* * DataProvider for illegal output test * Attemps to write some malformed chars, expects MalformedInputException */ - @DataProvider(name = "illegalOutput") - public Object[][] getWriteParameters() { - return new Object[][]{ - {testIllegalOutput, StandardCharsets.UTF_8.name(), null}, - {testIllegalOutput, null, StandardCharsets.UTF_8} - }; + public static Stream illegalOutput() { + return Stream.of + (Arguments.of(testIllegalOutput, StandardCharsets.UTF_8.name(), null), + Arguments.of(testIllegalOutput, null, StandardCharsets.UTF_8) + ); } /** @@ -140,7 +142,8 @@ public class EncodingTest { * @param charsetReader the charset for creating the reader * @throws Exception */ - @Test(dataProvider = "writeAndRead") + @ParameterizedTest + @MethodSource("writeAndRead") public void testWriteAndRead(String file, String csnWriter, Charset charsetWriter, String csnReader, Charset charsetReader) throws Exception { writeToFile(data, file, csnWriter, charsetWriter); @@ -148,7 +151,7 @@ public class EncodingTest { String result1 = readFileToString(file, csnReader, null); String result2 = readFileToString(file, null, charsetReader); - Assert.assertEquals(result1, result2); + assertEquals(result1, result2); } /** @@ -162,11 +165,14 @@ public class EncodingTest { * @param charsetReader the charset for creating the reader * @throws Exception */ - @Test(dataProvider = "illegalInput", expectedExceptions = MalformedInputException.class) + @ParameterizedTest + @MethodSource( "illegalInput") void testMalformedInput(String file, String csnWriter, Charset charsetWriter, - String csnReader, Charset charsetReader) throws Exception { + String csnReader, Charset charsetReader) + throws Exception { writeToFile(data, file, csnWriter, charsetWriter); - readFileToString(file, csnReader, charsetReader); + assertThrows(MalformedInputException.class, + () -> readFileToString(file, csnReader, charsetReader)); } /** @@ -178,23 +184,22 @@ public class EncodingTest { * @param charset the charset * @throws Exception */ - @Test(dataProvider = "illegalOutput", expectedExceptions = MalformedInputException.class) + @ParameterizedTest + @MethodSource("illegalOutput") public void testMalformedOutput(String fileName, String csn, Charset charset) - throws Exception { + throws Exception { try (FileOutputStream fos = new FileOutputStream(fileName); - WritableByteChannel wbc = (WritableByteChannel) fos.getChannel();) { - Writer writer; - if (csn != null) { - writer = Channels.newWriter(wbc, csn); - } else { - writer = Channels.newWriter(wbc, charset); - } - - for (int i = 0; i < ITERATIONS; i++) { - writer.write(illChars); - } - writer.flush(); - writer.close(); + WritableByteChannel wbc = (WritableByteChannel) fos.getChannel();) { + Charset cs = (csn != null) ? Charset.forName(csn) : charset; + Writer writer = Channels.newWriter(wbc, cs); + assertThrows(MalformedInputException.class, () -> { + try (writer) { + for (int i = 0; i < ITERATIONS; i++) { + writer.write(illChars); + } + writer.flush(); + } + }); } } diff --git a/test/jdk/java/nio/channels/Channels/ReadXBytes.java b/test/jdk/java/nio/channels/Channels/ReadXBytes.java index 1ea500f355d..4b296bb021e 100644 --- a/test/jdk/java/nio/channels/Channels/ReadXBytes.java +++ b/test/jdk/java/nio/channels/Channels/ReadXBytes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -30,7 +30,7 @@ * @library /test/lib * @build jdk.test.lib.RandomFactory * @modules java.base/jdk.internal.util - * @run testng/othervm/timeout=900 -Xmx12G ReadXBytes + * @run junit/othervm/timeout=900 -Xmx12g ReadXBytes * @key randomness */ import java.io.File; @@ -48,20 +48,24 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.Random; +import java.util.stream.IntStream; import jdk.internal.util.ArraysSupport; import static java.nio.file.StandardOpenOption.*; import jdk.test.lib.RandomFactory; -import org.testng.Assert; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; public class ReadXBytes { @@ -193,7 +197,7 @@ public class ReadXBytes { (length, cis) -> { byte[] bytes = cis.readAllBytes(); assertNotNull(bytes); - assertEquals(bytes.length, 0L); + assertEquals(0, bytes.length); } ); } @@ -206,7 +210,7 @@ public class ReadXBytes { cis.skipNBytes(length); byte[] bytes = cis.readAllBytes(); assertNotNull(bytes); - assertEquals(bytes.length, 0); + assertEquals(0, bytes.length); } ); } @@ -218,9 +222,9 @@ public class ReadXBytes { (length, cis, fis) -> { byte[] cisBytes = cis.readAllBytes(); assertNotNull(cisBytes); - assertEquals(cisBytes.length, (long)length); + assertEquals(length, cisBytes.length); byte[] fisBytes = fis.readAllBytes(); - assertEquals(cisBytes, fisBytes); + assertArrayEquals(fisBytes, cisBytes); } ); } @@ -236,20 +240,19 @@ public class ReadXBytes { ); } - // Provides an array of lengths - @DataProvider - public Object[][] lengthProvider() throws IOException { - return new Object[][] { - {1 + RAND.nextInt(1)}, - {1 + RAND.nextInt(Byte.MAX_VALUE)}, - {1 + RAND.nextInt(Short.MAX_VALUE)}, - {1 + RAND.nextInt(1_000_000)}, - {1 + RAND.nextInt(BIG_LENGTH)} - }; + // Provides a stream of lengths + public static IntStream fileLengths() throws IOException { + return IntStream.of + (1 + RAND.nextInt(1), + 1 + RAND.nextInt(Byte.MAX_VALUE), + 1 + RAND.nextInt(Short.MAX_VALUE), + 1 + RAND.nextInt(1_000_000), + 1 + RAND.nextInt(BIG_LENGTH)); } // Verifies readAllBytes() accuracy for random lengths and initial positions - @Test(dataProvider = "lengthProvider") + @ParameterizedTest + @MethodSource("fileLengths") public void readAllBytes(int len) throws IOException { dataTest(len, (length) -> createFileWithRandomContent(length), (length, cis, fis) -> { @@ -257,10 +260,10 @@ public class ReadXBytes { cis.skipNBytes(position); byte[] cisBytes = cis.readAllBytes(); assertNotNull(cisBytes); - assertEquals(cisBytes.length, length - position); + assertEquals(length - position, cisBytes.length); fis.skipNBytes(position); byte[] fisBytes = fis.readAllBytes(); - assertEquals(cisBytes, fisBytes); + assertArrayEquals(fisBytes, cisBytes); } ); } @@ -285,7 +288,7 @@ public class ReadXBytes { (length, cis) -> { byte[] bytes = cis.readNBytes(1); assertNotNull(bytes); - assertEquals(bytes.length, 0); + assertEquals(0, bytes.length); } ); } @@ -298,7 +301,7 @@ public class ReadXBytes { cis.skipNBytes(length); byte[] bytes = cis.readNBytes(1); assertNotNull(bytes); - assertEquals(bytes.length, 0); + assertEquals(0, bytes.length); } ); } @@ -310,9 +313,9 @@ public class ReadXBytes { (length, cis, fis) -> { byte[] cisBytes = cis.readNBytes(BIG_LENGTH); assertNotNull(cisBytes); - assertEquals(cisBytes.length, (long)length); + assertEquals(length, cisBytes.length); byte[] fisBytes = fis.readNBytes(BIG_LENGTH); - assertEquals(cisBytes, fisBytes); + assertArrayEquals(fisBytes, cisBytes); } ); } @@ -327,16 +330,17 @@ public class ReadXBytes { cis.skipNBytes(BIG_LENGTH); byte[] cisBytes = cis.readNBytes(n); assertNotNull(cisBytes); - assertEquals(cisBytes.length, n); + assertEquals(n, cisBytes.length); fis.skipNBytes(BIG_LENGTH); byte[] fisBytes = fis.readNBytes(n); - assertEquals(cisBytes, fisBytes); + assertArrayEquals(fisBytes, cisBytes); } ); } // Verifies readNBytes() accuracy for random lengths and initial positions - @Test(dataProvider = "lengthProvider") + @ParameterizedTest + @MethodSource("fileLengths") public void readNBytes(int len) throws IOException { dataTest(len, (length) -> createFileWithRandomContent(length), (length, cis, fis) -> { @@ -346,10 +350,10 @@ public class ReadXBytes { cis.skipNBytes(position); byte[] cisBytes = cis.readNBytes(n); assertNotNull(cisBytes); - assertEquals(cisBytes.length, n); + assertEquals(n, cisBytes.length); fis.skipNBytes(position); byte[] fisBytes = fis.readNBytes(n); - assertEquals(cisBytes, fisBytes); + assertArrayEquals(fisBytes, cisBytes); } ); } diff --git a/test/jdk/java/nio/channels/Channels/SocketChannelStreams.java b/test/jdk/java/nio/channels/Channels/SocketChannelStreams.java index 519de1e725f..0eab36dc3ee 100644 --- a/test/jdk/java/nio/channels/Channels/SocketChannelStreams.java +++ b/test/jdk/java/nio/channels/Channels/SocketChannelStreams.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -25,7 +25,7 @@ * @bug 8279339 8371718 * @summary Exercise InputStream/OutputStream returned by Channels.newXXXStream * when channel is a SocketChannel - * @run testng SocketChannelStreams + * @run junit SocketChannelStreams */ import java.io.Closeable; @@ -48,101 +48,112 @@ import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import org.testng.annotations.*; -import static org.testng.Assert.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; -@Test public class SocketChannelStreams { // Maximum size of internal temporary buffer private static final int MAX_BUFFER_SIZE = 128*1024; - private ScheduledExecutorService executor; + private static ScheduledExecutorService executor; - @BeforeClass() - public void init() { + @BeforeAll() + public static void init() { executor = Executors.newSingleThreadScheduledExecutor(); } - @AfterClass - public void finish() { + @AfterAll + public static void finish() { executor.shutdown(); } /** * Test read when bytes are available. */ + @Test public void testRead1() throws Exception { withConnection((sc, peer) -> { write(peer, 99); int n = Channels.newInputStream(sc).read(); - assertEquals(n, 99); + assertEquals(99, n); }); } /** * Test read blocking before bytes are available. */ + @Test public void testRead2() throws Exception { withConnection((sc, peer) -> { scheduleWrite(peer, 99, 1000); int n = Channels.newInputStream(sc).read(); - assertEquals(n, 99); + assertEquals(99, n); }); } /** * Test read after peer has closed connection. */ + @Test public void testRead3() throws Exception { withConnection((sc, peer) -> { peer.close(); int n = Channels.newInputStream(sc).read(); - assertEquals(n, -1); + assertEquals(-1, n); }); } /** * Test read blocking before peer closes connection. */ + @Test public void testRead4() throws Exception { withConnection((sc, peer) -> { scheduleClose(peer, 1000); int n = Channels.newInputStream(sc).read(); - assertEquals(n, -1); + assertEquals(-1, n); }); } /** * Test async close of channel when thread blocked in read. */ + @Test public void testRead5() throws Exception { withConnection((sc, peer) -> { scheduleClose(sc, 2000); InputStream in = Channels.newInputStream(sc); - expectThrows(IOException.class, () -> in.read()); + assertThrows(IOException.class, () -> in.read()); }); } /** * Test async close of input stream, when thread blocked in read. */ + @Test public void testRead6() throws Exception { withConnection((sc, peer) -> { InputStream in = Channels.newInputStream(sc); scheduleClose(in, 2000); - expectThrows(IOException.class, () -> in.read()); + assertThrows(IOException.class, () -> in.read()); }); } /** * Test interrupted status set before read. */ + @Test public void testRead7() throws Exception { withConnection((sc, peer) -> { Thread.currentThread().interrupt(); try { InputStream in = Channels.newInputStream(sc); - expectThrows(IOException.class, () -> in.read()); + assertThrows(IOException.class, () -> in.read()); } finally { Thread.interrupted(); // clear interrupt } @@ -153,12 +164,13 @@ public class SocketChannelStreams { /** * Test interrupt of thread blocked in read. */ + @Test public void testRead8() throws Exception { withConnection((sc, peer) -> { Future interrupter = scheduleInterrupt(Thread.currentThread(), 2000); try { InputStream in = Channels.newInputStream(sc); - expectThrows(IOException.class, () -> in.read()); + assertThrows(IOException.class, () -> in.read()); } finally { interrupter.cancel(true); Thread.interrupted(); // clear interrupt @@ -170,35 +182,38 @@ public class SocketChannelStreams { /** * Test that read is untimed when SO_TIMEOUT is set on the Socket adaptor. */ + @Test public void testRead9() throws Exception { withConnection((sc, peer) -> { sc.socket().setSoTimeout(100); scheduleWrite(peer, 99, 2000); // read should block until bytes are available int b = Channels.newInputStream(sc).read(); - assertTrue(b == 99); + assertEquals(99, b); }); } /** * Test write. */ + @Test public void testWrite1() throws Exception { withConnection((sc, peer) -> { OutputStream out = Channels.newOutputStream(sc); out.write(99); int n = read(peer); - assertEquals(n, 99); + assertEquals(99, n); }); } /** * Test async close of channel when thread blocked in write. */ + @Test public void testWrite2() throws Exception { withConnection((sc, peer) -> { scheduleClose(sc, 2000); - expectThrows(IOException.class, () -> { + assertThrows(IOException.class, () -> { OutputStream out = Channels.newOutputStream(sc); byte[] data = new byte[64*1000]; while (true) { @@ -211,11 +226,12 @@ public class SocketChannelStreams { /** * Test async close of output stream when thread blocked in write. */ + @Test public void testWrite3() throws Exception { withConnection((sc, peer) -> { OutputStream out = Channels.newOutputStream(sc); scheduleClose(out, 2000); - expectThrows(IOException.class, () -> { + assertThrows(IOException.class, () -> { byte[] data = new byte[64*1000]; while (true) { out.write(data); @@ -227,12 +243,13 @@ public class SocketChannelStreams { /** * Test interrupted status set before write. */ + @Test public void testWrite4() throws Exception { withConnection((sc, peer) -> { Thread.currentThread().interrupt(); try { OutputStream out = Channels.newOutputStream(sc); - expectThrows(IOException.class, () -> out.write(99)); + assertThrows(IOException.class, () -> out.write(99)); } finally { Thread.interrupted(); // clear interrupt } @@ -243,11 +260,12 @@ public class SocketChannelStreams { /** * Test interrupt of thread blocked in write. */ + @Test public void testWrite5() throws Exception { withConnection((sc, peer) -> { Future interrupter = scheduleInterrupt(Thread.currentThread(), 2000); try { - expectThrows(IOException.class, () -> { + assertThrows(IOException.class, () -> { OutputStream out = Channels.newOutputStream(sc); byte[] data = new byte[64*1000]; while (true) { @@ -266,6 +284,7 @@ public class SocketChannelStreams { * Test read when another thread is blocked in write. The read should * complete immediately. */ + @Test public void testConcurrentReadWrite1() throws Exception { withConnection((sc, peer) -> { InputStream in = Channels.newInputStream(sc); @@ -283,7 +302,7 @@ public class SocketChannelStreams { // test read, should not be blocked by writer thread write(peer, 99); int n = in.read(); - assertEquals(n, 99); + assertEquals(99, n); }); } @@ -291,6 +310,7 @@ public class SocketChannelStreams { * Test read when another thread is blocked in write. The read should * block until bytes are available. */ + @Test public void testConcurrentReadWrite2() throws Exception { withConnection((sc, peer) -> { InputStream in = Channels.newInputStream(sc); @@ -308,13 +328,14 @@ public class SocketChannelStreams { // test read, should not be blocked by writer thread scheduleWrite(peer, 99, 500); int n = in.read(); - assertEquals(n, 99); + assertEquals(99, n); }); } /** * Test writing when another thread is blocked in read. */ + @Test public void testConcurrentReadWrite3() throws Exception { withConnection((sc, peer) -> { InputStream in = Channels.newInputStream(sc); @@ -329,74 +350,79 @@ public class SocketChannelStreams { // test write, should not be blocked by reader thread out.write(99); int n = read(peer); - assertEquals(n, 99); + assertEquals(99, n); }); } /** * Test read/write when channel configured non-blocking. */ + @Test public void testIllegalBlockingMode() throws Exception { withConnection((sc, peer) -> { InputStream in = Channels.newInputStream(sc); OutputStream out = Channels.newOutputStream(sc); sc.configureBlocking(false); - expectThrows(IllegalBlockingModeException.class, () -> in.read()); - expectThrows(IllegalBlockingModeException.class, () -> out.write(99)); + assertThrows(IllegalBlockingModeException.class, () -> in.read()); + assertThrows(IllegalBlockingModeException.class, () -> out.write(99)); }); } /** * Test NullPointerException. */ + @Test public void testNullPointerException() throws Exception { withConnection((sc, peer) -> { InputStream in = Channels.newInputStream(sc); OutputStream out = Channels.newOutputStream(sc); - expectThrows(NullPointerException.class, () -> in.read(null)); - expectThrows(NullPointerException.class, () -> in.read(null, 0, 0)); + assertThrows(NullPointerException.class, () -> in.read(null)); + assertThrows(NullPointerException.class, () -> in.read(null, 0, 0)); - expectThrows(NullPointerException.class, () -> out.write(null)); - expectThrows(NullPointerException.class, () -> out.write(null, 0, 0)); + assertThrows(NullPointerException.class, () -> out.write(null)); + assertThrows(NullPointerException.class, () -> out.write(null, 0, 0)); }); } /** * Test IndexOutOfBoundsException. */ + @Test public void testIndexOutOfBoundsException() throws Exception { withConnection((sc, peer) -> { InputStream in = Channels.newInputStream(sc); OutputStream out = Channels.newOutputStream(sc); byte[] ba = new byte[100]; - expectThrows(IndexOutOfBoundsException.class, () -> in.read(ba, -1, 1)); - expectThrows(IndexOutOfBoundsException.class, () -> in.read(ba, 0, -1)); - expectThrows(IndexOutOfBoundsException.class, () -> in.read(ba, 0, 1000)); - expectThrows(IndexOutOfBoundsException.class, () -> in.read(ba, 1, 100)); + assertThrows(IndexOutOfBoundsException.class, () -> in.read(ba, -1, 1)); + assertThrows(IndexOutOfBoundsException.class, () -> in.read(ba, 0, -1)); + assertThrows(IndexOutOfBoundsException.class, () -> in.read(ba, 0, 1000)); + assertThrows(IndexOutOfBoundsException.class, () -> in.read(ba, 1, 100)); - expectThrows(IndexOutOfBoundsException.class, () -> out.write(ba, -1, 1)); - expectThrows(IndexOutOfBoundsException.class, () -> out.write(ba, 0, -1)); - expectThrows(IndexOutOfBoundsException.class, () -> out.write(ba, 0, 1000)); - expectThrows(IndexOutOfBoundsException.class, () -> out.write(ba, 1, 100)); + assertThrows(IndexOutOfBoundsException.class, () -> out.write(ba, -1, 1)); + assertThrows(IndexOutOfBoundsException.class, () -> out.write(ba, 0, -1)); + assertThrows(IndexOutOfBoundsException.class, () -> out.write(ba, 0, 1000)); + assertThrows(IndexOutOfBoundsException.class, () -> out.write(ba, 1, 100)); }); } /** * Test that internal buffers have at most MAX_BUFFER_SIZE bytes remaining. */ + @Test public void testReadLimit() throws IOException { InputStream in = Channels.newInputStream(new TestChannel()); byte[] b = new byte[3*MAX_BUFFER_SIZE]; int n = in.read(b, 0, b.length); - assertEquals(n, MAX_BUFFER_SIZE); + assertEquals(MAX_BUFFER_SIZE, n); } /** * Test that internal buffers have at most MAX_BUFFER_SIZE bytes remaining. */ + @Test public void testWriteLimit() throws IOException { OutputStream out = Channels.newOutputStream(new TestChannel()); byte[] b = new byte[3*MAX_BUFFER_SIZE]; diff --git a/test/jdk/java/nio/channels/Channels/TransferTo.java b/test/jdk/java/nio/channels/Channels/TransferTo.java index b02bf7b3649..a9626978dbe 100644 --- a/test/jdk/java/nio/channels/Channels/TransferTo.java +++ b/test/jdk/java/nio/channels/Channels/TransferTo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -37,17 +37,20 @@ import java.nio.file.Path; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; +import java.util.stream.Stream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -import static org.testng.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @library /test/lib * @build jdk.test.lib.RandomFactory - * @run testng/othervm/timeout=720 TransferTo + * @run junit/othervm/timeout=720 TransferTo * @bug 8265891 * @summary Tests whether sun.nio.ChannelInputStream.transferTo conforms to the * InputStream.transferTo specification @@ -59,41 +62,37 @@ public class TransferTo extends TransferToBase { * Provides test scenarios, i.e., combinations of input and output streams * to be tested. */ - @DataProvider - public static Object[][] streamCombinations() { - return new Object[][] { - // tests FileChannel.transferTo(FileChannel) optimized case - {fileChannelInput(), fileChannelOutput()}, + public static Stream streamCombinations() { + return Stream.of + (// tests FileChannel.transferTo(FileChannel) optimized case + Arguments.of(fileChannelInput(), fileChannelOutput()), - // tests FileChannel.transferTo(SelectableChannelOutput) - // optimized case - {fileChannelInput(), selectableChannelOutput()}, + // tests FileChannel.transferTo(SelectableChannelOutput) + // optimized case + Arguments.of(fileChannelInput(), selectableChannelOutput()), - // tests FileChannel.transferTo(WritableByteChannelOutput) - // optimized case - {fileChannelInput(), writableByteChannelOutput()}, + // tests FileChannel.transferTo(WritableByteChannelOutput) + // optimized case + Arguments.of(fileChannelInput(), writableByteChannelOutput()), - // tests InputStream.transferTo(OutputStream) default case - {readableByteChannelInput(), defaultOutput()} - }; + // tests InputStream.transferTo(OutputStream) default case + Arguments.of(readableByteChannelInput(), defaultOutput())); } /* * Input streams to be tested. */ - @DataProvider - public static Object[][] inputStreamProviders() { - return new Object[][] { - {fileChannelInput()}, - {readableByteChannelInput()} - }; + public static Stream inputStreamProviders() { + return Stream.of(Arguments.of(fileChannelInput()), + Arguments.of(readableByteChannelInput())); } /* * Testing API compliance: input stream must throw NullPointerException * when parameter "out" is null. */ - @Test(dataProvider = "inputStreamProviders") + @ParameterizedTest + @MethodSource("inputStreamProviders") public void testNullPointerException(InputStreamProvider inputStreamProvider) { assertNullPointerException(inputStreamProvider); } @@ -102,7 +101,8 @@ public class TransferTo extends TransferToBase { * Testing API compliance: complete content of input stream must be * transferred to output stream. */ - @Test(dataProvider = "streamCombinations") + @ParameterizedTest + @MethodSource("streamCombinations") public void testStreamContents(InputStreamProvider inputStreamProvider, OutputStreamProvider outputStreamProvider) throws Exception { assertStreamContents(inputStreamProvider, outputStreamProvider); diff --git a/test/jdk/java/nio/channels/Channels/TransferTo2.java b/test/jdk/java/nio/channels/Channels/TransferTo2.java index bd7e13c3a9b..0e02803c8d3 100644 --- a/test/jdk/java/nio/channels/Channels/TransferTo2.java +++ b/test/jdk/java/nio/channels/Channels/TransferTo2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -25,9 +25,11 @@ import java.io.OutputStream; import java.io.IOException; import java.nio.channels.Channels; import java.nio.channels.Pipe; +import java.util.stream.Stream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import static java.lang.String.format; @@ -35,7 +37,7 @@ import static java.lang.String.format; * @test * @library /test/lib * @build jdk.test.lib.RandomFactory - * @run testng/othervm/timeout=180 TransferTo2 + * @run junit/othervm/timeout=180 TransferTo2 * @bug 8278268 * @summary Tests FileChannel.transferFrom() optimized case * @key randomness @@ -46,33 +48,29 @@ public class TransferTo2 extends TransferToBase { * Provides test scenarios, i.e., combinations of input and output streams * to be tested. */ - @DataProvider - public static Object[][] streamCombinations() { - return new Object[][] { - // tests FileChannel.transferFrom(SelectableChannelOutput) optimized case - {selectableChannelInput(), fileChannelOutput()}, + public static Stream streamCombinations() { + return Stream.of + (// tests FileChannel.transferFrom(SelectableChannelOutput) optimized case + Arguments.of(selectableChannelInput(), fileChannelOutput()), - // tests FileChannel.transferFrom(ReadableByteChannelInput) optimized case - {readableByteChannelInput(), fileChannelOutput()}, - }; + // tests FileChannel.transferFrom(ReadableByteChannelInput) optimized case + Arguments.of(readableByteChannelInput(), fileChannelOutput())); } /* * Input streams to be tested. */ - @DataProvider - public static Object[][] inputStreamProviders() { - return new Object[][] { - {selectableChannelInput()}, - {readableByteChannelInput()} - }; + public static Stream inputStreamProviders() { + return Stream.of(Arguments.of(selectableChannelInput()), + Arguments.of(readableByteChannelInput())); } /* * Testing API compliance: input stream must throw NullPointerException * when parameter "out" is null. */ - @Test(dataProvider = "inputStreamProviders") + @ParameterizedTest + @MethodSource("inputStreamProviders") public void testNullPointerException(InputStreamProvider inputStreamProvider) { assertNullPointerException(inputStreamProvider); } @@ -81,7 +79,8 @@ public class TransferTo2 extends TransferToBase { * Testing API compliance: complete content of input stream must be * transferred to output stream. */ - @Test(dataProvider = "streamCombinations") + @ParameterizedTest + @MethodSource("streamCombinations") public void testStreamContents(InputStreamProvider inputStreamProvider, OutputStreamProvider outputStreamProvider) throws Exception { assertStreamContents(inputStreamProvider, outputStreamProvider); diff --git a/test/jdk/java/nio/channels/Channels/TransferToBase.java b/test/jdk/java/nio/channels/Channels/TransferToBase.java index 53ca283ae78..482fb1f262c 100644 --- a/test/jdk/java/nio/channels/Channels/TransferToBase.java +++ b/test/jdk/java/nio/channels/Channels/TransferToBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -43,9 +43,9 @@ import jdk.test.lib.RandomFactory; import static java.lang.String.format; import static java.nio.file.StandardOpenOption.*; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; class TransferToBase { static final int MIN_SIZE = 10_000; @@ -88,7 +88,7 @@ class TransferToBase { long reported = in.transferTo(out); int count = inBytes.length - posIn; - assertEquals(reported, count, format("reported %d bytes but should report %d", reported, count)); + assertEquals(count, reported); byte[] outBytes = recorder.get().get(); assertTrue(Arrays.equals(inBytes, posIn, posIn + count, outBytes, posOut, posOut + count), @@ -238,11 +238,11 @@ class TransferToBase { // compare reported transferred bytes, must be 3 GB // less the value of the initial position - assertEquals(count, BYTES_WRITTEN - initPos); + assertEquals(BYTES_WRITTEN - initPos, count); } // compare content of both files, failing if different - assertEquals(Files.mismatch(sourceFile, targetFile), -1); + assertEquals(-1, Files.mismatch(sourceFile, targetFile)); } finally { Files.delete(targetFile); diff --git a/test/jdk/java/nio/channels/Channels/TransferTo_2GB_transferFrom.java b/test/jdk/java/nio/channels/Channels/TransferTo_2GB_transferFrom.java index f57d6cca963..1dc17f6f5fc 100644 --- a/test/jdk/java/nio/channels/Channels/TransferTo_2GB_transferFrom.java +++ b/test/jdk/java/nio/channels/Channels/TransferTo_2GB_transferFrom.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -30,13 +30,13 @@ import java.nio.file.Files; import static java.nio.file.StandardOpenOption.*; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; /* * @test * @library /test/lib * @build jdk.test.lib.RandomFactory - * @run testng/othervm/timeout=720 TransferTo_2GB_transferFrom + * @run junit/othervm/timeout=720 TransferTo_2GB_transferFrom * @bug 8278268 * @summary Tests if ChannelInputStream.transferFrom correctly * transfers 2GB+ using FileChannel.transferFrom(ReadableByteChannel). diff --git a/test/jdk/java/nio/channels/Channels/TransferTo_2GB_transferTo.java b/test/jdk/java/nio/channels/Channels/TransferTo_2GB_transferTo.java index fdd3bddb126..174b89bd988 100644 --- a/test/jdk/java/nio/channels/Channels/TransferTo_2GB_transferTo.java +++ b/test/jdk/java/nio/channels/Channels/TransferTo_2GB_transferTo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -29,13 +29,13 @@ import java.nio.file.Files; import static java.nio.file.StandardOpenOption.*; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; /* * @test * @library /test/lib * @build jdk.test.lib.RandomFactory - * @run testng/othervm/timeout=720 TransferTo_2GB_transferTo + * @run junit/othervm/timeout=720 TransferTo_2GB_transferTo * @bug 8265891 * @summary Tests if ChannelInputStream.transferTo correctly * transfers 2GB+ using FileChannel.transferTo(WritableByteChannel). diff --git a/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java b/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java index 8d74fd8a387..a2f5844ce25 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java +++ b/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -22,7 +22,7 @@ */ /* - * @test + * @test id=default * @bug 8239355 8242885 8240901 * @key randomness * @summary Check that it is possible to send and receive datagrams of @@ -30,14 +30,41 @@ * @library /test/lib * @build jdk.test.lib.net.IPSupport * @run testng/othervm SendReceiveMaxSize + */ +/* + * @test id=preferIPv4Stack + * @key randomness + * @summary Check that it is possible to send and receive datagrams of + * maximum size on macOS, using an IPv4 only socket. + * @library /test/lib + * @build jdk.test.lib.net.IPSupport * @run testng/othervm -Djava.net.preferIPv4Stack=true SendReceiveMaxSize */ +/* + * @test id=preferIPv6Loopback + * @key randomness + * @summary Check that it is possible to send and receive datagrams of + * maximum size on macOS, using a dual socket and the loopback + * interface. + * @library /test/lib + * @build jdk.test.lib.net.IPSupport + * @run testng/othervm -Dtest.preferLoopback=true SendReceiveMaxSize + */ +/* + * @test id=preferIPv4Loopback + * @key randomness + * @summary Check that it is possible to send and receive datagrams of + * maximum size on macOS, using an IPv4 only socket and the + * loopback interface + * @library /test/lib + * @build jdk.test.lib.net.IPSupport + * @run testng/othervm -Dtest.preferLoopback=true -Djava.net.preferIPv4Stack=true SendReceiveMaxSize + */ import jdk.test.lib.RandomFactory; import jdk.test.lib.NetworkConfiguration; import jdk.test.lib.Platform; import jdk.test.lib.net.IPSupport; -import org.testng.Assert; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -68,6 +95,7 @@ import static org.testng.Assert.assertTrue; public class SendReceiveMaxSize { private final static Class IOE = IOException.class; private final static Random random = RandomFactory.getRandom(); + private final static boolean PREFER_LOOPBACK = Boolean.getBoolean("test.preferLoopback"); public interface DatagramChannelSupplier { DatagramChannel open() throws IOException; @@ -83,11 +111,14 @@ public class SendReceiveMaxSize { public Object[][] invariants() throws IOException { var testcases = new ArrayList(); var nc = NetworkConfiguration.probe(); + var ipv4Loopback = (Inet4Address) InetAddress.getByName("127.0.0.1"); + var ipv6Loopback = (Inet6Address) InetAddress.getByName("::1"); if (hasIPv4()) { - InetAddress IPv4Addr = nc.ip4Addresses() + InetAddress IPv4Addr = PREFER_LOOPBACK ? ipv4Loopback + : nc.ip4Addresses() .filter(Predicate.not(InetAddress::isLoopbackAddress)) .findFirst() - .orElse((Inet4Address) InetAddress.getByName("127.0.0.1")); + .orElse(ipv4Loopback); testcases.add(new Object[]{ supplier(() -> DatagramChannel.open()), IPSupport.getMaxUDPSendBufSizeIPv4(), @@ -100,10 +131,11 @@ public class SendReceiveMaxSize { }); } if (!preferIPv4Stack() && hasIPv6()) { - InetAddress IPv6Addr = nc.ip6Addresses() + InetAddress IPv6Addr = PREFER_LOOPBACK ? ipv6Loopback + : nc.ip6Addresses() .filter(Predicate.not(InetAddress::isLoopbackAddress)) .findFirst() - .orElse((Inet6Address) InetAddress.getByName("::1")); + .orElse(ipv6Loopback); testcases.add(new Object[]{ supplier(() -> DatagramChannel.open()), IPSupport.getMaxUDPSendBufSizeIPv6(), diff --git a/test/jdk/java/nio/channels/FileChannel/MapToMemorySegmentTest.java b/test/jdk/java/nio/channels/FileChannel/MapToMemorySegmentTest.java index de89e318fc3..bd2097176ca 100644 --- a/test/jdk/java/nio/channels/FileChannel/MapToMemorySegmentTest.java +++ b/test/jdk/java/nio/channels/FileChannel/MapToMemorySegmentTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -24,7 +24,7 @@ /* @test * @bug 8281412 * @summary Test FileChannel::map to MemorySegment with custom file channel - * @run testng/othervm MapToMemorySegmentTest + * @run junit/othervm MapToMemorySegmentTest */ import java.io.File; @@ -39,10 +39,10 @@ import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.file.Path; import java.nio.file.StandardOpenOption; -import org.testng.annotations.Test; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; public class MapToMemorySegmentTest { @@ -58,13 +58,14 @@ public class MapToMemorySegmentTest { } } - @Test(expectedExceptions = UnsupportedOperationException.class) + @Test public void testCustomFileChannel() throws IOException { var arena = Arena.ofConfined(); var fc = FileChannel.open(tempPath, StandardOpenOption.WRITE, StandardOpenOption.READ); var fileChannel = new CustomFileChannel(fc); - try (arena; fileChannel){ - fileChannel.map(FileChannel.MapMode.READ_WRITE, 1L, 10L, arena); + try (arena; fileChannel) { + assertThrows(UnsupportedOperationException.class, + () -> fileChannel.map(FileChannel.MapMode.READ_WRITE, 1L, 10L, arena)); } } diff --git a/test/jdk/java/nio/channels/FileChannel/Transfer.java b/test/jdk/java/nio/channels/FileChannel/Transfer.java index 51adba60b06..1afc38a61bd 100644 --- a/test/jdk/java/nio/channels/FileChannel/Transfer.java +++ b/test/jdk/java/nio/channels/FileChannel/Transfer.java @@ -27,7 +27,7 @@ * @library .. * @library /test/lib * @build jdk.test.lib.RandomFactory - * @run testng/timeout=300 Transfer + * @run junit/timeout=300 Transfer * @key randomness */ @@ -54,8 +54,13 @@ import java.util.concurrent.TimeUnit; import jdk.test.lib.RandomFactory; -import org.testng.Assert; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; public class Transfer { @@ -83,25 +88,22 @@ public class Transfer { long oldSourcePosition = sourceChannel.position(); long bytesWritten = sinkChannel.transferFrom(sourceChannel, 0, 10); - if (bytesWritten != 10) - throw new RuntimeException("Transfer failed"); + assertEquals(10, bytesWritten, "Transfer failed"); - if (sourceChannel.position() == oldSourcePosition) - throw new RuntimeException("Source position didn't change"); + assertNotEquals(oldSourcePosition, sourceChannel.position(), + "Source position didn't change"); - if (sinkChannel.position() != oldSinkPosition) - throw new RuntimeException("Sink position changed"); + assertEquals(oldSinkPosition, sinkChannel.position(), + "Sink position changed"); - if (sinkChannel.size() != 10) - throw new RuntimeException("Unexpected sink size"); + assertEquals(10, sinkChannel.size(), "Unexpected sink size"); bytesWritten = sinkChannel.transferFrom(sourceChannel, 1000, 10); - if (bytesWritten > 10) - throw new RuntimeException("Wrote too many bytes"); + assertFalse(bytesWritten > 10, "Wrote too many bytes"); - if (sinkChannel.size() != 1000 + bytesWritten) - throw new RuntimeException("Unexpected sink size"); + assertEquals(1000 + bytesWritten, sinkChannel.size(), + "Unexpected sink size"); sourceChannel.close(); sinkChannel.close(); @@ -130,8 +132,7 @@ public class Transfer { int totalWritten = 0; while (totalWritten < size + 10) { int written = sink.write(outgoingdata); - if (written < 0) - throw new Exception("Write failed"); + assertTrue(written >= 0, "Write failed"); totalWritten += written; } @@ -143,14 +144,11 @@ public class Transfer { long bytesWritten = fc.transferFrom(source, 0, size); fc.force(true); - if (bytesWritten != size) - throw new RuntimeException("Transfer failed"); + assertEquals(size, bytesWritten, "Transfer failed"); - if (fc.position() != oldPosition) - throw new RuntimeException("Position changed"); + assertEquals(oldPosition, fc.position(), "Position changed"); - if (fc.size() != size) - throw new RuntimeException("Unexpected sink size "+ fc.size()); + assertEquals(size, fc.size(), "Unexpected sink size "+ fc.size()); fc.close(); sink.close(); @@ -168,7 +166,7 @@ public class Transfer { CharSequence csq = "Reality is greater than the sum of its parts."; Files.writeString(source.toPath(), csq); final long length = csq.length(); - Assert.assertEquals(source.length(), length); + assertEquals(length, source.length()); File target = File.createTempFile("before", "after"); target.deleteOnExit(); @@ -183,7 +181,7 @@ public class Transfer { long n = chSource.transferTo(length, 16385, chTarget); // At the end of the input so no bytes should be transferred - Assert.assertEquals(n, 0); + assertEquals(0, n); } } @@ -251,8 +249,7 @@ public class Transfer { fc1.transferTo(0, srcData.length + 1, fc2); - if (fc2.size() > 4) - throw new Exception("xferTest03 failed"); + assertFalse(fc2.size() > 4, "xferTest03 failed"); fc1.close(); fc2.close(); @@ -273,9 +270,7 @@ public class Transfer { while ((c = r.read()) != -1) sb.append((char)c); String contents = sb.toString(); - if (! contents.equals(expected)) - throw new Exception("expected: " + expected - + ", got: " + contents); + assertEquals(expected, contents); r.close(); } @@ -301,8 +296,7 @@ public class Transfer { new RandomAccessFile(sink, "rw").getChannel(); long n = sinkChannel.transferFrom(sourceChannel, 0L, sourceChannel.size()); // overflow - if (n != remaining) - throw new Exception("n == " + n + ", remaining == " + remaining); + assertEquals(remaining, n); sinkChannel.close(); sourceChannel.close(); @@ -363,9 +357,8 @@ public class Transfer { FileChannel fc1 = new FileOutputStream(source).getChannel(); FileChannel fc2 = new RandomAccessFile(target, "rw").getChannel(); try { - fc2.transferFrom(fc1, 0L, 0); - throw new RuntimeException("NonReadableChannelException expected"); - } catch (NonReadableChannelException expected) { + assertThrows(NonReadableChannelException.class, + () -> fc2.transferFrom(fc1, 0L, 0)); } finally { fc1.close(); fc2.close(); diff --git a/test/jdk/java/nio/channels/FileChannel/Transfer4GBFile.java b/test/jdk/java/nio/channels/FileChannel/Transfer4GBFile.java index b8c026c983c..0766f4ffb45 100644 --- a/test/jdk/java/nio/channels/FileChannel/Transfer4GBFile.java +++ b/test/jdk/java/nio/channels/FileChannel/Transfer4GBFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -25,7 +25,7 @@ * @bug 4638365 * @summary Test FileChannel.transferFrom and transferTo for 4GB files * @build FileChannelUtils - * @run testng/timeout=300 Transfer4GBFile + * @run junit/timeout=300 Transfer4GBFile */ import java.io.BufferedWriter; @@ -38,10 +38,12 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.concurrent.TimeUnit; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import static java.nio.file.StandardOpenOption.*; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class Transfer4GBFile { private static PrintStream err = System.err; @@ -71,10 +73,8 @@ public class Transfer4GBFile { long bytesWritten = sourceChannel.transferTo(testSize - 40, 10, sinkChannel); - if (bytesWritten != 10) { - throw new RuntimeException("Transfer test 4 failed " + - bytesWritten); - } + assertEquals(10, bytesWritten, + "Transfer test 4 failed " + bytesWritten); } Files.delete(source); @@ -112,10 +112,8 @@ public class Transfer4GBFile { FileChannel sinkChannel = FileChannel.open(sink, WRITE)) { long bytesWritten = sinkChannel.transferFrom(sourceChannel, testSize - 40, 10); - if (bytesWritten != 10) { - throw new RuntimeException("Transfer test 5 failed " + - bytesWritten); - } + assertEquals(10, bytesWritten, + "Transfer test 5 failed " + bytesWritten); } Files.delete(source); diff --git a/test/jdk/java/nio/channels/FileChannel/TransferTo6GBFile.java b/test/jdk/java/nio/channels/FileChannel/TransferTo6GBFile.java index 9ca6df0c870..791a0718a75 100644 --- a/test/jdk/java/nio/channels/FileChannel/TransferTo6GBFile.java +++ b/test/jdk/java/nio/channels/FileChannel/TransferTo6GBFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -25,7 +25,7 @@ * @bug 6253145 * @summary Test FileChannel.transferTo with file positions up to 8GB * @build FileChannelUtils - * @run testng/timeout=300 TransferTo6GBFile + * @run junit/timeout=300 TransferTo6GBFile */ import java.io.IOException; @@ -40,10 +40,14 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.concurrent.TimeUnit; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import static java.nio.file.StandardOpenOption.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + public class TransferTo6GBFile { private static PrintStream err = System.err; @@ -126,16 +130,14 @@ public class TransferTo6GBFile { long nread = 0; while (nread < count) { int n = source.read(readbuf); - if (n < 0) - throw new RuntimeException("Premature EOF!"); + assertTrue(n >= 0, "Premature EOF!"); nread += n; } // check reply from echo server readbuf.flip(); sendbuf.flip(); - if (!readbuf.equals(sendbuf)) - throw new RuntimeException("Echoed bytes do not match!"); + assertEquals(sendbuf, readbuf, "Echoed bytes do not match!"); readbuf.clear(); sendbuf.clear(); } diff --git a/test/jdk/java/nio/channels/FileLock/Overlaps.java b/test/jdk/java/nio/channels/FileLock/Overlaps.java index 64293e2996d..3e3b92ac67e 100644 --- a/test/jdk/java/nio/channels/FileLock/Overlaps.java +++ b/test/jdk/java/nio/channels/FileLock/Overlaps.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -25,7 +25,7 @@ * @test * @bug 5041655 * @summary Verify FileLock.overlaps - * @run testng Overlaps + * @run junit Overlaps */ import java.io.IOException; import java.nio.ByteBuffer; @@ -33,15 +33,18 @@ import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.file.Files; import java.nio.file.Path; +import java.util.stream.Stream; import static java.lang.Boolean.*; import static java.nio.file.StandardOpenOption.*; -import static org.testng.Assert.assertEquals; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class Overlaps { private static final long POS = 27; @@ -49,47 +52,46 @@ public class Overlaps { private static FileChannel fc; - @BeforeClass - public void before() throws IOException { + @BeforeAll + public static void before() throws IOException { Path path = Files.createTempFile(Path.of("."), "foo", ".bar"); fc = FileChannel.open(path, CREATE, WRITE, DELETE_ON_CLOSE); fc.position(POS); fc.write(ByteBuffer.wrap(new byte[(int)SIZE])); } - @AfterClass - public void after() throws IOException { + @AfterAll + public static void after() throws IOException { fc.close(); } - @DataProvider - public Object[][] ranges() { - return new Object[][] { - {POS, SIZE, -1,-1, FALSE}, - {POS, SIZE, 0, -1, FALSE}, - {POS, SIZE, POS - 1, -1, FALSE}, - {POS, SIZE, POS + SIZE/2, -1, FALSE}, - {POS, SIZE, POS + SIZE, -1, FALSE}, - {POS, SIZE, -1, POS, FALSE}, - {POS, SIZE, -1, POS + SIZE/2, TRUE}, - {POS, SIZE, POS - 2, 1, FALSE}, - {POS, SIZE, POS + 1, 1, TRUE}, - {POS, SIZE, POS + SIZE/2, 0, TRUE}, - {POS, SIZE, Long.MAX_VALUE, 2, FALSE}, - {POS, SIZE, POS + SIZE / 2, Long.MAX_VALUE, TRUE}, - {POS, SIZE, 0, 0, TRUE}, - {Long.MAX_VALUE - SIZE/2, 0, 0, SIZE, FALSE}, - {Long.MAX_VALUE - SIZE/2, 0, Long.MAX_VALUE - SIZE/4, SIZE, TRUE}, - {Long.MAX_VALUE - SIZE/2, 0, Long.MAX_VALUE - SIZE, 0, TRUE}, - {Long.MAX_VALUE - SIZE, 0, Long.MAX_VALUE - SIZE/2, 0, TRUE} - }; + public static Stream ranges() { + return Stream.of( + Arguments.of(POS, SIZE, -1, -1, FALSE), + Arguments.of(POS, SIZE, 0, -1, FALSE), + Arguments.of(POS, SIZE, POS - 1, -1, FALSE), + Arguments.of(POS, SIZE, POS + SIZE/2, -1, FALSE), + Arguments.of(POS, SIZE, POS + SIZE, -1, FALSE), + Arguments.of(POS, SIZE, -1, POS, FALSE), + Arguments.of(POS, SIZE, -1, POS + SIZE/2, TRUE), + Arguments.of(POS, SIZE, POS - 2, 1, FALSE), + Arguments.of(POS, SIZE, POS + 1, 1, TRUE), + Arguments.of(POS, SIZE, POS + SIZE/2, 0, TRUE), + Arguments.of(POS, SIZE, Long.MAX_VALUE, 2, FALSE), + Arguments.of(POS, SIZE, POS + SIZE / 2, Long.MAX_VALUE, TRUE), + Arguments.of(POS, SIZE, 0, 0, TRUE), + Arguments.of(Long.MAX_VALUE - SIZE/2, 0, 0, SIZE, FALSE), + Arguments.of(Long.MAX_VALUE - SIZE/2, 0, Long.MAX_VALUE - SIZE/4, SIZE, TRUE), + Arguments.of(Long.MAX_VALUE - SIZE/2, 0, Long.MAX_VALUE - SIZE, 0, TRUE), + Arguments.of(Long.MAX_VALUE - SIZE, 0, Long.MAX_VALUE - SIZE/2, 0, TRUE)); } - @Test(dataProvider = "ranges") + @ParameterizedTest + @MethodSource("ranges") public void overlaps(long lockPos, long lockSize, long pos, long size, boolean overlaps) throws IOException { try (FileLock lock = fc.lock(lockPos, lockSize, false)) { - assertEquals(lock.overlaps(pos, size), overlaps); + assertEquals(overlaps, lock.overlaps(pos, size)); } } } diff --git a/test/jdk/java/nio/channels/SelectionKey/AtomicUpdates.java b/test/jdk/java/nio/channels/SelectionKey/AtomicUpdates.java index 8e5bee51b71..e609530ff69 100644 --- a/test/jdk/java/nio/channels/SelectionKey/AtomicUpdates.java +++ b/test/jdk/java/nio/channels/SelectionKey/AtomicUpdates.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -23,7 +23,7 @@ /* @test * @bug 6350055 - * @run testng AtomicUpdates + * @run junit AtomicUpdates * @summary Unit test for SelectionKey interestOpsOr and interestOpsAnd */ @@ -37,15 +37,19 @@ import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.Test; import static java.nio.channels.SelectionKey.OP_READ; import static java.nio.channels.SelectionKey.OP_WRITE; import static java.nio.channels.SelectionKey.OP_CONNECT; import static java.nio.channels.SelectionKey.OP_ACCEPT; -import static org.testng.Assert.*; -@Test +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; + public class AtomicUpdates { private SelectionKey keyFor(SocketChannel sc) { @@ -94,74 +98,69 @@ public class AtomicUpdates { } private void test(SelectionKey key) { - assertTrue(key.channel() instanceof SocketChannel); + assertInstanceOf(SocketChannel.class, key.channel()); key.interestOps(0); // 0 -> 0 int previous = key.interestOpsOr(0); - assertTrue(previous == 0); - assertTrue(key.interestOps() == 0); + assertEquals(0, previous); + assertEquals(0, key.interestOps()); // 0 -> OP_CONNECT previous = key.interestOpsOr(OP_CONNECT); - assertTrue(previous == 0); - assertTrue(key.interestOps() == OP_CONNECT); + assertEquals(0, previous); + assertEquals(OP_CONNECT, key.interestOps()); // OP_CONNECT -> OP_CONNECT previous = key.interestOpsOr(0); - assertTrue(previous == OP_CONNECT); - assertTrue(key.interestOps() == OP_CONNECT); + assertEquals(OP_CONNECT, previous); + assertEquals(OP_CONNECT, key.interestOps()); // OP_CONNECT -> OP_CONNECT | OP_READ | OP_WRITE previous = key.interestOpsOr(OP_READ | OP_WRITE); - assertTrue(previous == OP_CONNECT); - assertTrue(key.interestOps() == (OP_CONNECT | OP_READ | OP_WRITE)); + assertEquals(OP_CONNECT, previous); + assertEquals(OP_CONNECT | OP_READ | OP_WRITE, key.interestOps()); // OP_CONNECT | OP_READ | OP_WRITE -> OP_CONNECT previous = key.interestOpsAnd(~(OP_READ | OP_WRITE)); - assertTrue(previous == (OP_CONNECT | OP_READ | OP_WRITE)); - assertTrue(key.interestOps() == OP_CONNECT); + assertEquals(OP_CONNECT | OP_READ | OP_WRITE, previous); + assertEquals(OP_CONNECT, key.interestOps()); // OP_CONNECT -> 0 previous = key.interestOpsAnd(~OP_CONNECT); - assertTrue(previous == OP_CONNECT); - assertTrue(key.interestOps() == 0); + assertEquals(OP_CONNECT, previous); + assertEquals(0, key.interestOps()); // OP_READ | OP_WRITE -> OP_READ | OP_WRITE key.interestOps(OP_READ | OP_WRITE); previous = key.interestOpsAnd(~OP_ACCEPT); - assertTrue(previous == (OP_READ | OP_WRITE)); - assertTrue(key.interestOps() == (OP_READ | OP_WRITE)); + assertEquals(OP_READ | OP_WRITE, previous); + assertEquals(OP_READ | OP_WRITE, key.interestOps()); // OP_READ | OP_WRITE -> 0 previous = key.interestOpsAnd(0); - assertTrue(previous == (OP_READ | OP_WRITE)); - assertTrue(key.interestOps() == 0); + assertEquals(OP_READ | OP_WRITE, previous); + assertEquals(0, key.interestOps()); // 0 -> 0 previous = key.interestOpsAnd(0); - assertTrue(previous == 0); - assertTrue(key.interestOps() == 0); + assertEquals(0, previous); + assertEquals(0, key.interestOps()); - try { - key.interestOpsOr(OP_ACCEPT); - fail("IllegalArgumentException expected"); - } catch (IllegalArgumentException expected) { } + assertThrows(IllegalArgumentException.class, + () -> key.interestOpsOr(OP_ACCEPT)); key.cancel(); - try { - key.interestOpsOr(OP_READ); - fail("CancelledKeyException expected"); - } catch (CancelledKeyException expected) { } - try { - key.interestOpsAnd(~OP_READ); - fail("CancelledKeyException expected"); - } catch (CancelledKeyException expected) { } + assertThrows(CancelledKeyException.class, + () -> key.interestOpsOr(OP_READ)); + assertThrows(CancelledKeyException.class, + () -> key.interestOpsAnd(~OP_READ)); } /** * Test default implementation of interestOpsOr/interestOpsAnd */ + @Test public void testDefaultImplementation() throws Exception { try (SocketChannel sc = SocketChannel.open()) { SelectionKey key = keyFor(sc); @@ -172,6 +171,7 @@ public class AtomicUpdates { /** * Test the default provider implementation of SelectionKey. */ + @Test public void testNioImplementation() throws Exception { try (SocketChannel sc = SocketChannel.open(); Selector sel = Selector.open()) { diff --git a/test/jdk/java/nio/channels/Selector/SelectWithConsumer.java b/test/jdk/java/nio/channels/Selector/SelectWithConsumer.java index 1a2ada51bce..b90a68ffd04 100644 --- a/test/jdk/java/nio/channels/Selector/SelectWithConsumer.java +++ b/test/jdk/java/nio/channels/Selector/SelectWithConsumer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -24,12 +24,12 @@ /* @test * @summary Unit test for Selector.select/selectNow(Consumer) * @bug 8199433 8208780 - * @run testng SelectWithConsumer + * @run junit SelectWithConsumer */ /* @test * @requires (os.family == "windows") - * @run testng/othervm -Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.WindowsSelectorProvider SelectWithConsumer + * @run junit/othervm -Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.WindowsSelectorProvider SelectWithConsumer */ import java.io.Closeable; @@ -49,11 +49,16 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import static java.util.concurrent.TimeUnit.*; -import org.testng.annotations.AfterTest; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; -@Test public class SelectWithConsumer { /** @@ -81,46 +86,47 @@ public class SelectWithConsumer { // select(Consumer) notifiedOps.set(0); int n = sel.select(k -> { - assertTrue(Thread.currentThread() == callerThread); - assertTrue(k == key); + assertSame(callerThread, Thread.currentThread()); + assertSame(key, k); int readyOps = key.readyOps(); - assertTrue((readyOps & interestOps) != 0); - assertTrue((readyOps & notifiedOps.get()) == 0); + assertNotEquals(0, readyOps & interestOps); + assertEquals(0, readyOps & notifiedOps.get()); notifiedOps.set(notifiedOps.get() | readyOps); }); assertTrue((n == 1) ^ (expectedOps == 0)); - assertTrue(notifiedOps.get() == expectedOps); + assertEquals(expectedOps, notifiedOps.get()); // select(Consumer, timeout) notifiedOps.set(0); n = sel.select(k -> { - assertTrue(Thread.currentThread() == callerThread); - assertTrue(k == key); + assertSame(callerThread, Thread.currentThread()); + assertSame(key, k); int readyOps = key.readyOps(); - assertTrue((readyOps & interestOps) != 0); - assertTrue((readyOps & notifiedOps.get()) == 0); + assertNotEquals(0, readyOps & interestOps); + assertEquals(0, readyOps & notifiedOps.get()); notifiedOps.set(notifiedOps.get() | readyOps); }, 1000); assertTrue((n == 1) ^ (expectedOps == 0)); - assertTrue(notifiedOps.get() == expectedOps); + assertEquals(expectedOps, notifiedOps.get()); // selectNow(Consumer) notifiedOps.set(0); n = sel.selectNow(k -> { - assertTrue(Thread.currentThread() == callerThread); - assertTrue(k == key); + assertSame(callerThread, Thread.currentThread()); + assertSame(key, k); int readyOps = key.readyOps(); - assertTrue((readyOps & interestOps) != 0); - assertTrue((readyOps & notifiedOps.get()) == 0); + assertNotEquals(0, readyOps & interestOps); + assertEquals(0, readyOps & notifiedOps.get()); notifiedOps.set(notifiedOps.get() | readyOps); }); assertTrue((n == 1) ^ (expectedOps == 0)); - assertTrue(notifiedOps.get() == expectedOps); + assertEquals(expectedOps, notifiedOps.get()); } /** * Test that an action is performed when a channel is ready for reading. */ + @Test public void testReadable() throws Exception { Pipe p = Pipe.open(); try (Selector sel = Selector.open()) { @@ -142,6 +148,7 @@ public class SelectWithConsumer { /** * Test that an action is performed when a channel is ready for writing. */ + @Test public void testWritable() throws Exception { Pipe p = Pipe.open(); try (Selector sel = Selector.open()) { @@ -161,6 +168,7 @@ public class SelectWithConsumer { * Test that an action is performed when a channel is ready for both * reading and writing. */ + @Test public void testReadableAndWriteable() throws Exception { ServerSocketChannel ssc = null; SocketChannel sc = null; @@ -188,6 +196,7 @@ public class SelectWithConsumer { /** * Test that the action is called for two selected channels */ + @Test public void testTwoChannels() throws Exception { Pipe p = Pipe.open(); try (Selector sel = Selector.open()) { @@ -217,8 +226,8 @@ public class SelectWithConsumer { assertTrue(k == key1 || k == key2); counter.incrementAndGet(); }); - assertTrue(n == 2); - assertTrue(counter.get() == 2); + assertEquals(2, n); + assertEquals(2, counter.get()); // select(Consumer, timeout) counter.set(0); @@ -226,8 +235,8 @@ public class SelectWithConsumer { assertTrue(k == key1 || k == key2); counter.incrementAndGet(); }, 1000); - assertTrue(n == 2); - assertTrue(counter.get() == 2); + assertEquals(2, n); + assertEquals(2, counter.get()); // selectNow(Consumer) counter.set(0); @@ -235,8 +244,8 @@ public class SelectWithConsumer { assertTrue(k == key1 || k == key2); counter.incrementAndGet(); }); - assertTrue(n == 2); - assertTrue(counter.get() == 2); + assertEquals(2, n); + assertEquals(2, counter.get()); } finally { closePipe(p); } @@ -245,6 +254,7 @@ public class SelectWithConsumer { /** * Test calling select twice, the action should be invoked each time */ + @Test public void testRepeatedSelect1() throws Exception { Pipe p = Pipe.open(); try (Selector sel = Selector.open()) { @@ -269,6 +279,7 @@ public class SelectWithConsumer { * Test calling select twice. An I/O operation is performed after the * first select so the channel will not be selected by the second select. */ + @Test public void testRepeatedSelect2() throws Exception { Pipe p = Pipe.open(); try (Selector sel = Selector.open()) { @@ -301,6 +312,7 @@ public class SelectWithConsumer { /** * Test timeout */ + @Test public void testTimeout() throws Exception { Pipe p = Pipe.open(); try (Selector sel = Selector.open()) { @@ -311,7 +323,7 @@ public class SelectWithConsumer { long start = millisTime(); int n = sel.select(k -> assertTrue(false), 1000L); expectDuration(start, 500, Long.MAX_VALUE); - assertTrue(n == 0); + assertEquals(0, n); } finally { closePipe(p); } @@ -320,12 +332,13 @@ public class SelectWithConsumer { /** * Test wakeup prior to select */ + @Test public void testWakeupBeforeSelect() throws Exception { // select(Consumer) try (Selector sel = Selector.open()) { sel.wakeup(); int n = sel.select(k -> assertTrue(false)); - assertTrue(n == 0); + assertEquals(0, n); } // select(Consumer, timeout) @@ -334,19 +347,20 @@ public class SelectWithConsumer { long start = millisTime(); int n = sel.select(k -> assertTrue(false), 60*1000); expectDuration(start, 0, 20_000); - assertTrue(n == 0); + assertEquals(0, n); } } /** * Test wakeup during select */ + @Test public void testWakeupDuringSelect() throws Exception { // select(Consumer) try (Selector sel = Selector.open()) { scheduleWakeup(sel, 1, SECONDS); int n = sel.select(k -> assertTrue(false)); - assertTrue(n == 0); + assertEquals(0, n); } // select(Consumer, timeout) @@ -355,19 +369,20 @@ public class SelectWithConsumer { long start = millisTime(); int n = sel.select(k -> assertTrue(false), 60*1000); expectDuration(start, 0, 20_000); - assertTrue(n == 0); + assertEquals(0, n); } } /** * Test invoking select with interrupted status set */ + @Test public void testInterruptBeforeSelect() throws Exception { // select(Consumer) try (Selector sel = Selector.open()) { Thread.currentThread().interrupt(); int n = sel.select(k -> assertTrue(false)); - assertTrue(n == 0); + assertEquals(0, n); assertTrue(Thread.currentThread().isInterrupted()); assertTrue(sel.isOpen()); } finally { @@ -380,7 +395,7 @@ public class SelectWithConsumer { long start = millisTime(); int n = sel.select(k -> assertTrue(false), 60*1000); expectDuration(start, 0, 20_000); - assertTrue(n == 0); + assertEquals(0, n); assertTrue(Thread.currentThread().isInterrupted()); assertTrue(sel.isOpen()); } finally { @@ -391,12 +406,13 @@ public class SelectWithConsumer { /** * Test interrupt thread during select */ + @Test public void testInterruptDuringSelect() throws Exception { // select(Consumer) try (Selector sel = Selector.open()) { scheduleInterrupt(Thread.currentThread(), 1, SECONDS); int n = sel.select(k -> assertTrue(false)); - assertTrue(n == 0); + assertEquals(0, n); assertTrue(Thread.currentThread().isInterrupted()); assertTrue(sel.isOpen()); } finally { @@ -407,7 +423,7 @@ public class SelectWithConsumer { try (Selector sel = Selector.open()) { scheduleInterrupt(Thread.currentThread(), 1, SECONDS); int n = sel.select(k -> assertTrue(false), 60*1000); - assertTrue(n == 0); + assertEquals(0, n); assertTrue(Thread.currentThread().isInterrupted()); assertTrue(sel.isOpen()); } finally { @@ -418,34 +434,38 @@ public class SelectWithConsumer { /** * Test invoking select on a closed selector */ - @Test(expectedExceptions = ClosedSelectorException.class) + @Test public void testClosedSelector1() throws Exception { Selector sel = Selector.open(); sel.close(); - sel.select(k -> assertTrue(false)); + assertThrows(ClosedSelectorException.class, + () -> sel.select(k -> assertTrue(false))); } - @Test(expectedExceptions = ClosedSelectorException.class) + @Test public void testClosedSelector2() throws Exception { Selector sel = Selector.open(); sel.close(); - sel.select(k -> assertTrue(false), 1000); + assertThrows(ClosedSelectorException.class, + () -> sel.select(k -> assertTrue(false), 1000)); } - @Test(expectedExceptions = ClosedSelectorException.class) + @Test public void testClosedSelector3() throws Exception { Selector sel = Selector.open(); sel.close(); - sel.selectNow(k -> assertTrue(false)); + assertThrows(ClosedSelectorException.class, + () -> sel.selectNow(k -> assertTrue(false))); } /** * Test closing selector while in a selection operation */ + @Test public void testCloseDuringSelect() throws Exception { // select(Consumer) try (Selector sel = Selector.open()) { scheduleClose(sel, 3, SECONDS); int n = sel.select(k -> assertTrue(false)); - assertTrue(n == 0); + assertEquals(0, n); assertFalse(sel.isOpen()); } @@ -458,7 +478,7 @@ public class SelectWithConsumer { long after = System.nanoTime(); long selectDuration = (after - start) / 1000000; long scheduleDuration = (start - before) / 1000000; - assertTrue(n == 0); + assertEquals(0, n); assertTrue(selectDuration > 2000 && selectDuration < 10*1000, "select took " + selectDuration + " ms schedule took " + scheduleDuration + " ms"); @@ -469,7 +489,7 @@ public class SelectWithConsumer { /** * Test action closing selector */ - @Test(expectedExceptions = ClosedSelectorException.class) + @Test public void testActionClosingSelector() throws Exception { Pipe p = Pipe.open(); try (Selector sel = Selector.open()) { @@ -482,12 +502,14 @@ public class SelectWithConsumer { sink.write(messageBuffer()); // should relay ClosedSelectorException - sel.select(k -> { - assertTrue(k == key); - try { - sel.close(); - } catch (IOException ioe) { } - }); + assertThrows(ClosedSelectorException.class, + () -> sel.select(k -> { + assertTrue(k == key); + try { + sel.close(); + } catch (IOException ioe) { } + }) + ); } finally { closePipe(p); } @@ -497,6 +519,7 @@ public class SelectWithConsumer { * Test that the action is invoked while synchronized on the selector and * its selected-key set. */ + @Test public void testLocks() throws Exception { Pipe p = Pipe.open(); try (Selector sel = Selector.open()) { @@ -510,7 +533,7 @@ public class SelectWithConsumer { // select(Consumer) sel.select(k -> { - assertTrue(k == key); + assertSame(key, k); assertTrue(Thread.holdsLock(sel)); assertFalse(Thread.holdsLock(sel.keys())); assertTrue(Thread.holdsLock(sel.selectedKeys())); @@ -518,7 +541,7 @@ public class SelectWithConsumer { // select(Consumer, timeout) sel.select(k -> { - assertTrue(k == key); + assertSame(key, k); assertTrue(Thread.holdsLock(sel)); assertFalse(Thread.holdsLock(sel.keys())); assertTrue(Thread.holdsLock(sel.selectedKeys())); @@ -526,7 +549,7 @@ public class SelectWithConsumer { // selectNow(Consumer) sel.selectNow(k -> { - assertTrue(k == key); + assertSame(key, k); assertTrue(Thread.holdsLock(sel)); assertFalse(Thread.holdsLock(sel.keys())); assertTrue(Thread.holdsLock(sel.selectedKeys())); @@ -540,6 +563,7 @@ public class SelectWithConsumer { * Test that selection operations remove cancelled keys from the selector's * key and selected-key sets. */ + @Test public void testCancel() throws Exception { Pipe p = Pipe.open(); try (Selector sel = Selector.open()) { @@ -569,7 +593,7 @@ public class SelectWithConsumer { // cancel key1 key1.cancel(); int n = sel.selectNow(k -> assertTrue(k == key2)); - assertTrue(n == 1); + assertEquals(1, n); assertFalse(sel.keys().contains(key1)); assertTrue(sel.keys().contains(key2)); assertFalse(sel.selectedKeys().contains(key1)); @@ -578,7 +602,7 @@ public class SelectWithConsumer { // cancel key2 key2.cancel(); n = sel.selectNow(k -> assertTrue(false)); - assertTrue(n == 0); + assertEquals(0, n); assertFalse(sel.keys().contains(key1)); assertFalse(sel.keys().contains(key2)); assertFalse(sel.selectedKeys().contains(key1)); @@ -591,6 +615,7 @@ public class SelectWithConsumer { /** * Test an action invoking select() */ + @Test public void testReentrantSelect1() throws Exception { Pipe p = Pipe.open(); try (Selector sel = Selector.open()) { @@ -611,7 +636,7 @@ public class SelectWithConsumer { } catch (IllegalStateException expected) { } }); - assertTrue(n == 1); + assertEquals(1, n); } finally { closePipe(p); } @@ -620,6 +645,7 @@ public class SelectWithConsumer { /** * Test an action invoking selectNow() */ + @Test public void testReentrantSelect2() throws Exception { Pipe p = Pipe.open(); try (Selector sel = Selector.open()) { @@ -640,7 +666,7 @@ public class SelectWithConsumer { } catch (IllegalStateException expected) { } }); - assertTrue(n == 1); + assertEquals(1, n); } finally { closePipe(p); } @@ -649,6 +675,7 @@ public class SelectWithConsumer { /** * Test an action invoking select(Consumer) */ + @Test public void testReentrantSelect3() throws Exception { Pipe p = Pipe.open(); try (Selector sel = Selector.open()) { @@ -669,7 +696,7 @@ public class SelectWithConsumer { } catch (IllegalStateException expected) { } }); - assertTrue(n == 1); + assertEquals(1, n); } finally { closePipe(p); } @@ -678,42 +705,46 @@ public class SelectWithConsumer { /** * Negative timeout */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testNegativeTimeout() throws Exception { try (Selector sel = Selector.open()) { - sel.select(k -> { }, -1L); + assertThrows(IllegalArgumentException.class, + () -> sel.select(k -> { }, -1L)); } } /** * Null action */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testNull1() throws Exception { try (Selector sel = Selector.open()) { - sel.select(null); + assertThrows(NullPointerException.class, + () -> sel.select(null)); } } - @Test(expectedExceptions = NullPointerException.class) + @Test public void testNull2() throws Exception { try (Selector sel = Selector.open()) { - sel.select(null, 1000); + assertThrows(NullPointerException.class, + () -> sel.select(null, 1000)); } } - @Test(expectedExceptions = NullPointerException.class) + @Test public void testNull3() throws Exception { try (Selector sel = Selector.open()) { - sel.selectNow(null); + assertThrows(NullPointerException.class, + () -> sel.selectNow(null)); } } // -- support methods --- - private final ScheduledExecutorService POOL = Executors.newScheduledThreadPool(1); + private static final ScheduledExecutorService POOL = Executors.newScheduledThreadPool(1); - @AfterTest - void shutdownThreadPool() { + @AfterAll + static void shutdownThreadPool() { POOL.shutdown(); } diff --git a/test/jdk/java/nio/channels/Selector/UpdateReadyOps.java b/test/jdk/java/nio/channels/Selector/UpdateReadyOps.java index 57a563c6a59..87b512fe495 100644 --- a/test/jdk/java/nio/channels/Selector/UpdateReadyOps.java +++ b/test/jdk/java/nio/channels/Selector/UpdateReadyOps.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -22,7 +22,7 @@ */ /* @test - * @run testng UpdateReadyOps + * @run junit UpdateReadyOps * @summary Test that the ready set from a selection operation is bitwise-disjoined * into a key's ready set when the key is already in the selected-key set */ @@ -37,16 +37,19 @@ import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; -@Test public class UpdateReadyOps { /** * Test that OP_WRITE is preserved when updating the ready set of a key in * the selected-key set to add OP_READ. */ + @Test public void testOpWritePreserved() throws Exception { try (ConnectionPair pair = new ConnectionPair(); Selector sel = Selector.open()) { @@ -58,14 +61,14 @@ public class UpdateReadyOps { SelectionKey key = sc1.register(sel, SelectionKey.OP_WRITE); int updated = sel.select(); - assertTrue(updated == 1); + assertEquals(1, updated); assertTrue(sel.selectedKeys().contains(key)); assertFalse(key.isReadable()); assertTrue(key.isWritable()); // select again, should be no updates updated = sel.select(); - assertTrue(updated == 0); + assertEquals(0, updated); assertTrue(sel.selectedKeys().contains(key)); assertFalse(key.isReadable()); assertTrue(key.isWritable()); @@ -78,16 +81,17 @@ public class UpdateReadyOps { key.interestOps(SelectionKey.OP_READ); updated = sel.select(); - assertTrue(updated == 1); - assertTrue(sel.selectedKeys().size() == 1); + assertEquals(1, updated); + assertEquals(1, sel.selectedKeys().size()); assertTrue(key.isReadable()); assertTrue(key.isWritable()); - assertTrue(key.readyOps() == (SelectionKey.OP_READ|SelectionKey.OP_WRITE)); + assertEquals(SelectionKey.OP_READ|SelectionKey.OP_WRITE, + key.readyOps()); // select again, should be no updates updated = sel.select(); - assertTrue(updated == 0); - assertTrue(sel.selectedKeys().size() == 1); + assertEquals(0, updated); + assertEquals(1, sel.selectedKeys().size()); assertTrue(key.isReadable()); assertTrue(key.isWritable()); } @@ -97,6 +101,7 @@ public class UpdateReadyOps { * Test that OP_READ is preserved when updating the ready set of a key in * the selected-key set to add OP_WRITE. */ + @Test public void testOpReadPreserved() throws Exception { try (ConnectionPair pair = new ConnectionPair(); Selector sel = Selector.open()) { @@ -111,32 +116,32 @@ public class UpdateReadyOps { sc2.write(helloMessage()); int updated = sel.select(); - assertTrue(updated == 1); - assertTrue(sel.selectedKeys().size() == 1); + assertEquals(1, updated); + assertEquals(1, sel.selectedKeys().size()); assertTrue(sel.selectedKeys().contains(key)); assertTrue(key.isReadable()); assertFalse(key.isWritable()); // select again, should be no updates updated = sel.select(); - assertTrue(updated == 0); + assertEquals(0, updated); assertTrue(sel.selectedKeys().contains(key)); assertTrue(key.isReadable()); assertFalse(key.isWritable()); key.interestOps(SelectionKey.OP_WRITE); updated = sel.select(); - assertTrue(updated == 1); - assertTrue(sel.selectedKeys().size() == 1); + assertEquals(1, updated); + assertEquals(1, sel.selectedKeys().size()); assertTrue(sel.selectedKeys().contains(key)); assertTrue(key.isReadable()); assertTrue(key.isWritable()); - assertTrue(key.readyOps() == (SelectionKey.OP_READ|SelectionKey.OP_WRITE)); + assertEquals(SelectionKey.OP_READ|SelectionKey.OP_WRITE, key.readyOps()); // select again, should be no updates updated = sel.select(); - assertTrue(updated == 0); - assertTrue(sel.selectedKeys().size() == 1); + assertEquals(0, updated); + assertEquals(1, sel.selectedKeys().size()); assertTrue(key.isReadable()); assertTrue(key.isWritable()); } diff --git a/test/jdk/java/nio/channels/spi/SelectorProvider/TestDefaultImplementation.java b/test/jdk/java/nio/channels/spi/SelectorProvider/TestDefaultImplementation.java index 6d402949982..e17b16dadc7 100644 --- a/test/jdk/java/nio/channels/spi/SelectorProvider/TestDefaultImplementation.java +++ b/test/jdk/java/nio/channels/spi/SelectorProvider/TestDefaultImplementation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -25,19 +25,22 @@ * @test * @bug 8254692 * @summary Basic test for java.nio.channels.spi.SelectorProvider.java default implementation - * @run testng TestDefaultImplementation + * @run junit TestDefaultImplementation */ -import org.testng.annotations.Test; - import java.io.IOException; import java.net.ProtocolFamily; -import java.nio.channels.*; +import java.nio.channels.DatagramChannel; +import java.nio.channels.Pipe; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; import java.nio.channels.spi.AbstractSelector; import java.nio.channels.spi.SelectorProvider; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertThrows; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; public class TestDefaultImplementation { static final Class UOE = UnsupportedOperationException.class; @@ -67,4 +70,4 @@ public class TestDefaultImplementation { @Override public ServerSocketChannel openServerSocketChannel() { return null; } @Override public SocketChannel openSocketChannel() { return null; } } -} \ No newline at end of file +} diff --git a/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/InheritedChannelTest.java b/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/InheritedChannelTest.java index 934bf509d88..897fc6236dc 100644 --- a/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/InheritedChannelTest.java +++ b/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/InheritedChannelTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -36,7 +36,7 @@ * UnixSocketTest StateTest StateTestService EchoTest EchoService * UnixDomainChannelTest CloseTest Launcher Util * CheckIPv6Test CheckIPv6Service - * @run testng/othervm/native InheritedChannelTest + * @run junit/othervm/native InheritedChannelTest * @key intermittent */ @@ -45,14 +45,16 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.stream.Stream; import jdk.test.lib.JDKToolFinder; import jdk.test.lib.Utils; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.Platform; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import static java.util.Arrays.asList; @@ -64,19 +66,18 @@ public class InheritedChannelTest { private static final Path libraryPath = Paths.get(System.getProperty("java.library.path")); - @DataProvider - public Object[][] testCases() { - return new Object[][] { - { "UnixDomainChannelTest", List.of(UnixDomainChannelTest.class.getName())}, - { "UnixSocketTest", List.of(UnixSocketTest.class.getName())}, - { "StateTest", List.of(StateTest.class.getName(), "-Dtest.classes="+TEST_CLASSES)}, - { "EchoTest", List.of(EchoTest.class.getName()) }, - { "CheckIPv6Test", List.of(CheckIPv6Test.class.getName()) }, - { "CloseTest", List.of(CloseTest.class.getName()) }, - }; + public static Stream testCases() { + return Stream.of + (Arguments.of( "UnixDomainChannelTest", List.of(UnixDomainChannelTest.class.getName())), + Arguments.of( "UnixSocketTest", List.of(UnixSocketTest.class.getName())), + Arguments.of( "StateTest", List.of(StateTest.class.getName(), "-Dtest.classes="+TEST_CLASSES)), + Arguments.of( "EchoTest", List.of(EchoTest.class.getName())), + Arguments.of( "CheckIPv6Test", List.of(CheckIPv6Test.class.getName())), + Arguments.of( "CloseTest", List.of(CloseTest.class.getName()))); } - @Test(dataProvider = "testCases") + @ParameterizedTest + @MethodSource("testCases") public void test(String desc, List opts) throws Throwable { String pathVar = Platform.sharedLibraryPathVariableName(); System.out.println(pathVar + "=" + libraryPath); diff --git a/test/jdk/java/nio/charset/Charset/DefaultCharsetTest.java b/test/jdk/java/nio/charset/Charset/DefaultCharsetTest.java index be33ecb70f2..f3caf94775e 100644 --- a/test/jdk/java/nio/charset/Charset/DefaultCharsetTest.java +++ b/test/jdk/java/nio/charset/Charset/DefaultCharsetTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -34,22 +34,19 @@ * jdk.test.lib.Platform * jdk.test.lib.process.* * Default - * @run testng DefaultCharsetTest + * @run junit DefaultCharsetTest */ -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; import java.util.Map; +import java.util.stream.Stream; -import jdk.test.lib.Platform; import jdk.test.lib.process.ProcessTools; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; public class DefaultCharsetTest { @@ -58,28 +55,27 @@ public class DefaultCharsetTest { private static final Map env = pb.environment(); private static String UNSUPPORTED = null; - @BeforeClass + @BeforeAll public static void checkSupports() throws Exception { UNSUPPORTED = runWithLocale("nonexist"); } - @DataProvider - public static Iterator locales() { - List data = new ArrayList<>(); - data.add(new String[]{"en_US", "iso-8859-1"}); - data.add(new String[]{"ja_JP.utf8", "utf-8"}); - data.add(new String[]{"tr_TR", "iso-8859-9"}); - data.add(new String[]{"C", "us-ascii"}); - data.add(new String[]{"ja_JP", "x-euc-jp-linux"}); - data.add(new String[]{"ja_JP.eucjp", "x-euc-jp-linux"}); - data.add(new String[]{"ja_JP.ujis", "x-euc-jp-linux"}); - data.add(new String[]{"ja_JP.utf8", "utf-8"}); - return data.iterator(); + public static Stream locales() { + return Stream.of( + Arguments.of("en_US", "iso-8859-1"), + Arguments.of("ja_JP.utf8", "utf-8"), + Arguments.of("tr_TR", "iso-8859-9"), + Arguments.of("C", "us-ascii"), + Arguments.of("ja_JP", "x-euc-jp-linux"), + Arguments.of("ja_JP.eucjp", "x-euc-jp-linux"), + Arguments.of("ja_JP.ujis", "x-euc-jp-linux"), + Arguments.of("ja_JP.utf8", "utf-8") + ); } - @Test(dataProvider = "locales") - public void testDefaultCharset(String locale, String expectedCharset) - throws Exception { + @ParameterizedTest + @MethodSource("locales") + public void testDefaultCharset(String locale, String expectedCharset) throws Exception { String actual = runWithLocale(locale); if (UNSUPPORTED.equals(actual)) { System.out.println(locale + ": Locale not supported, skipping..."); diff --git a/test/jdk/java/nio/charset/Charset/ForName.java b/test/jdk/java/nio/charset/Charset/ForName.java index d8a1e2f72d3..6af903a3c8a 100644 --- a/test/jdk/java/nio/charset/Charset/ForName.java +++ b/test/jdk/java/nio/charset/Charset/ForName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -25,49 +25,51 @@ * @summary Unit test for forName(String, Charset) * @bug 8270490 * @modules jdk.charsets - * @run testng ForName + * @run junit ForName */ import java.nio.charset.Charset; -import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.StandardCharsets; +import java.util.stream.Stream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; -@Test public class ForName { - @DataProvider - Object[][] params() { - return new Object[][] { - {"UTF-8", null, StandardCharsets.UTF_8}, - {"UTF-8", StandardCharsets.US_ASCII, StandardCharsets.UTF_8}, - {"windows-31j", StandardCharsets.US_ASCII, Charset.forName("windows-31j")}, - {"foo", StandardCharsets.US_ASCII, StandardCharsets.US_ASCII}, - {"foo", null, null}, - {"\u3042", null, null}, - {"\u3042", StandardCharsets.UTF_8, StandardCharsets.UTF_8}, - }; + static Stream params() { + return Stream.of( + Arguments.of("UTF-8", null, StandardCharsets.UTF_8), + Arguments.of("UTF-8", StandardCharsets.US_ASCII, StandardCharsets.UTF_8), + Arguments.of("windows-31j", StandardCharsets.US_ASCII, Charset.forName("windows-31j")), + Arguments.of("foo", StandardCharsets.US_ASCII, StandardCharsets.US_ASCII), + Arguments.of("foo", null, null), + Arguments.of("\u3042", null, null), + Arguments.of("\u3042", StandardCharsets.UTF_8, StandardCharsets.UTF_8) + ); } - @DataProvider - Object[][] paramsIAE() { - return new Object[][] { - {null, null}, - {null, StandardCharsets.UTF_8}, - }; + static Stream paramsIAE() { + return Stream.of( + Arguments.of(null, null), + Arguments.of(null, StandardCharsets.UTF_8) + ); } - @Test(dataProvider="params") - public void testForName_2arg(String name, Charset fallback, Charset expected) throws Exception { + @ParameterizedTest + @MethodSource("params") + public void testForName_2arg(String name, Charset fallback, Charset expected) { var cs = Charset.forName(name, fallback); - assertEquals(cs, expected); + assertEquals(expected, cs); } - @Test(dataProvider="paramsIAE", expectedExceptions=IllegalArgumentException.class) - public void testForName_2arg_IAE(String name, Charset fallback) throws Exception { - Charset.forName(name, fallback); + @ParameterizedTest + @MethodSource("paramsIAE") + public void testForName_2arg_IAE(String name, Charset fallback) { + assertThrows(IllegalArgumentException.class, () -> Charset.forName(name, fallback)); } } diff --git a/test/jdk/java/nio/charset/CharsetDecoder/CoderMalfunctionErrorTest.java b/test/jdk/java/nio/charset/CharsetDecoder/CoderMalfunctionErrorTest.java index 79091071ea3..5e830b955b6 100644 --- a/test/jdk/java/nio/charset/CharsetDecoder/CoderMalfunctionErrorTest.java +++ b/test/jdk/java/nio/charset/CharsetDecoder/CoderMalfunctionErrorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -23,26 +23,30 @@ /* @test * @bug 8253832 - * @run testng CoderMalfunctionErrorTest * @summary Check CoderMalfunctionError is thrown for any RuntimeException * on CharsetDecoder.decodeLoop() invocation. + * @run junit CoderMalfunctionErrorTest */ -import org.testng.annotations.Test; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.*; -@Test +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; + public class CoderMalfunctionErrorTest { - @Test (expectedExceptions = CoderMalfunctionError.class) + + @Test public void testDecodeLoop() { - new CharsetDecoder(StandardCharsets.US_ASCII, 1, 1) { + assertThrows(CoderMalfunctionError.class, + () -> new CharsetDecoder(StandardCharsets.US_ASCII, 1, 1) { @Override protected CoderResult decodeLoop(ByteBuffer byteBuffer, CharBuffer charBuffer) { throw new RuntimeException("This exception should be wrapped in CoderMalfunctionError"); } - }.decode(null, null, true); + }.decode(null, null, true)); } } diff --git a/test/jdk/java/nio/charset/CharsetEncoder/CoderMalfunctionErrorTest.java b/test/jdk/java/nio/charset/CharsetEncoder/CoderMalfunctionErrorTest.java index ffd73164d9f..e917126d461 100644 --- a/test/jdk/java/nio/charset/CharsetEncoder/CoderMalfunctionErrorTest.java +++ b/test/jdk/java/nio/charset/CharsetEncoder/CoderMalfunctionErrorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -23,26 +23,29 @@ /* @test * @bug 8253832 - * @run testng CoderMalfunctionErrorTest + * @run junit CoderMalfunctionErrorTest * @summary Check CoderMalfunctionError is thrown for any RuntimeException * on CharsetEncoder.encodeLoop() invocation. */ -import org.testng.annotations.Test; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.*; -@Test +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; + public class CoderMalfunctionErrorTest { - @Test (expectedExceptions = CoderMalfunctionError.class) + @Test public void testEncodeLoop() { - new CharsetEncoder(StandardCharsets.US_ASCII, 1, 1) { + assertThrows(CoderMalfunctionError.class, + () -> new CharsetEncoder(StandardCharsets.US_ASCII, 1, 1) { @Override protected CoderResult encodeLoop(CharBuffer charBuffer, ByteBuffer byteBuffer) { throw new RuntimeException("This exception should be wrapped in CoderMalfunctionError"); } - }.encode(null, null, true); + }.encode(null, null, true)); } } diff --git a/test/jdk/java/nio/charset/spi/CharsetProviderBasicTest.java b/test/jdk/java/nio/charset/spi/CharsetProviderBasicTest.java index abf46d81b1c..8b0e74d4360 100644 --- a/test/jdk/java/nio/charset/spi/CharsetProviderBasicTest.java +++ b/test/jdk/java/nio/charset/spi/CharsetProviderBasicTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -35,28 +35,25 @@ * jdk.test.lib.util.JarUtils * FooCharset FooProvider CharsetTest * @run driver SetupJar - * @run testng CharsetProviderBasicTest + * @run junit CharsetProviderBasicTest */ import java.io.File; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.stream.Stream; import jdk.test.lib.JDKToolFinder; import jdk.test.lib.Utils; import jdk.test.lib.process.ProcessTools; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import static java.util.Arrays.asList; public class CharsetProviderBasicTest { - private static final String TEST_SRC = System.getProperty("test.src"); - private static final List DEFAULT_CSS = List.of( "US-ASCII", "8859_1", "iso-ir-6", "UTF-16", "windows-1252", "!BAR", "cp1252" ); @@ -69,14 +66,15 @@ public class CharsetProviderBasicTest { .equals(locale); } - @DataProvider - public static Iterator testCases() { - return Stream.of("", "ja_JP.eucJP", "tr_TR") - .map(locale -> new Object[]{locale, "FOO"}) - .iterator(); + public static Stream testCases() { + return Stream.of("", + "ja_JP.eucJP", + "tr_TR") + .map(locale -> Arguments.of(locale, "FOO")); } - @Test(dataProvider = "testCases") + @ParameterizedTest + @MethodSource("testCases") public void testDefaultCharset(String locale, String css) throws Throwable { if ((System.getProperty("os.name").startsWith("Windows") || !checkSupports(locale)) && (!locale.isEmpty())) { diff --git a/test/jdk/java/nio/file/Files/BytesAndLines.java b/test/jdk/java/nio/file/Files/BytesAndLines.java index ca486ab87fc..1d51021e786 100644 --- a/test/jdk/java/nio/file/Files/BytesAndLines.java +++ b/test/jdk/java/nio/file/Files/BytesAndLines.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, 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 @@ -24,38 +24,47 @@ /* @test * @bug 7006126 8020669 8024788 8019526 * @build BytesAndLines PassThroughFileSystem - * @run testng BytesAndLines + * @run junit BytesAndLines * @summary Unit test for methods for Files readAllBytes, readAllLines and * and write methods. * @key randomness */ -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.OpenOption; -import static java.nio.file.StandardOpenOption.*; +import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.CharacterCodingException; import java.nio.charset.MalformedInputException; import java.nio.charset.UnmappableCharacterException; -import static java.nio.charset.StandardCharsets.*; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.file.Files; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Random; import java.util.concurrent.Callable; -import java.io.IOException; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static java.nio.charset.StandardCharsets.*; +import static java.nio.file.StandardOpenOption.*; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; -@Test(groups = "unit") public class BytesAndLines { // data for text files @@ -66,15 +75,15 @@ public class BytesAndLines { private static Random RAND = new Random(); // file used by most tests - private Path tmpfile; + private static Path tmpfile; - @BeforeClass - void setup() throws IOException { + @BeforeAll + static void setup() throws IOException { tmpfile = Files.createTempFile("blah", null); } - @AfterClass - void cleanup() throws IOException { + @AfterAll + static void cleanup() throws IOException { Files.deleteIfExists(tmpfile); } @@ -90,6 +99,7 @@ public class BytesAndLines { /** * Exercise NullPointerException */ + @Test public void testNulls() { Path file = Paths.get("foo"); byte[] bytes = new byte[100]; @@ -118,18 +128,13 @@ public class BytesAndLines { } private void checkNullPointerException(Callable c) { - try { - c.call(); - fail("NullPointerException expected"); - } catch (NullPointerException ignore) { - } catch (Exception e) { - fail(e + " not expected"); - } + assertThrows(NullPointerException.class, () -> c.call()); } /** * Exercise Files.readAllBytes(Path) on varied file sizes */ + @Test public void testReadAllBytes() throws IOException { int size = 0; while (size <= 16*1024) { @@ -145,7 +150,7 @@ public class BytesAndLines { // check expected bytes are read byte[] read = Files.readAllBytes(tmpfile); - assertTrue(Arrays.equals(read, expected), "Bytes read not the same as written"); + assertArrayEquals(expected, read, "Bytes read not the same as written"); } /** @@ -153,13 +158,14 @@ public class BytesAndLines { * special because file sizes are reported as 0 even though the file * has content. */ + @Test + @EnabledOnOs(OS.LINUX) public void testReadAllBytesOnProcFS() throws IOException { // read from procfs - if (System.getProperty("os.name").equals("Linux")) { - Path statFile = Paths.get("/proc/self/stat"); - byte[] data = Files.readAllBytes(statFile); - assertTrue(data.length > 0, "Files.readAllBytes('" + statFile + "') failed to read"); - } + Path statFile = Paths.get("/proc/self/stat"); + byte[] data = Files.readAllBytes(statFile); + assertTrue(data.length > 0, + "Files.readAllBytes('" + statFile + "') failed to read"); } /** @@ -167,6 +173,7 @@ public class BytesAndLines { * because readAllBytes was originally implemented to use FileChannel * and so may not be supported by custom file system providers. */ + @Test public void testReadAllBytesOnCustomFS() throws IOException { Path myfile = PassThroughFileSystem.create().getPath("myfile"); try { @@ -175,7 +182,7 @@ public class BytesAndLines { byte[] b1 = genBytes(size); Files.write(myfile, b1); byte[] b2 = Files.readAllBytes(myfile); - assertTrue(Arrays.equals(b1, b2), "bytes not equal"); + assertArrayEquals(b1, b2, "bytes not equal"); size += 512; } } finally { @@ -186,6 +193,7 @@ public class BytesAndLines { /** * Exercise Files.write(Path, byte[], OpenOption...) on various sizes */ + @Test public void testWriteBytes() throws IOException { int size = 0; while (size < 16*1024) { @@ -198,10 +206,10 @@ public class BytesAndLines { private void testWriteBytes(int size, boolean append) throws IOException { byte[] bytes = genBytes(size); Path result = Files.write(tmpfile, bytes); - assertTrue(result == tmpfile); + assertSame(tmpfile, result); if (append) { Files.write(tmpfile, bytes, APPEND); - assertTrue(Files.size(tmpfile) == size*2); + assertEquals(size*2, Files.size(tmpfile)); } byte[] expected; @@ -214,12 +222,13 @@ public class BytesAndLines { } byte[] read = Files.readAllBytes(tmpfile); - assertTrue(Arrays.equals(read, expected), "Bytes read not the same as written"); + assertArrayEquals(expected, read, "Bytes read not the same as written"); } /** * Exercise Files.readAllLines(Path, Charset) */ + @Test public void testReadAllLines() throws IOException { // zero lines Files.write(tmpfile, new byte[0]); @@ -230,23 +239,22 @@ public class BytesAndLines { byte[] hi = { (byte)'h', (byte)'i' }; Files.write(tmpfile, hi); lines = Files.readAllLines(tmpfile, US_ASCII); - assertTrue(lines.size() == 1, "One line expected"); - assertTrue(lines.get(0).equals("hi"), "'Hi' expected"); + assertEquals(1, lines.size(), "One line expected"); + assertEquals("hi", lines.get(0), "'Hi' expected"); // two lines using platform's line separator List expected = Arrays.asList("hi", "there"); Files.write(tmpfile, expected, US_ASCII); assertTrue(Files.size(tmpfile) > 0, "File is empty"); lines = Files.readAllLines(tmpfile, US_ASCII); - assertTrue(lines.equals(expected), "Unexpected lines"); + assertLinesMatch(expected, lines, "Unexpected lines"); // MalformedInputException byte[] bad = { (byte)0xff, (byte)0xff }; Files.write(tmpfile, bad); - try { - Files.readAllLines(tmpfile, US_ASCII); - fail("MalformedInputException expected"); - } catch (MalformedInputException ignore) { } + assertThrows(MalformedInputException.class, + () -> Files.readAllLines(tmpfile, US_ASCII), + "MalformedInputException expected"); } /** @@ -254,24 +262,27 @@ public class BytesAndLines { * is special because file sizes are reported as 0 even though the file * has content. */ + @Test + @EnabledOnOs(OS.LINUX) public void testReadAllLinesOnProcFS() throws IOException { - if (System.getProperty("os.name").equals("Linux")) { - Path statFile = Paths.get("/proc/self/stat"); - List lines = Files.readAllLines(statFile); - assertTrue(lines.size() > 0, "Files.readAllLines('" + statFile + "') failed to read"); - } + Path statFile = Paths.get("/proc/self/stat"); + List lines = Files.readAllLines(statFile); + assertTrue(lines.size() > 0, + "Files.readAllLines('" + statFile + "') failed to read"); } /** * Exercise Files.readAllLines(Path) */ + @Test public void testReadAllLinesUTF8() throws IOException { Files.write(tmpfile, encodeAsUTF8(EN_STRING + "\n" + JA_STRING)); List lines = Files.readAllLines(tmpfile); - assertTrue(lines.size() == 2, "Read " + lines.size() + " lines instead of 2"); - assertTrue(lines.get(0).equals(EN_STRING)); - assertTrue(lines.get(1).equals(JA_STRING)); + assertEquals(2, lines.size(), + "Read " + lines.size() + " lines instead of 2"); + assertEquals(EN_STRING, lines.get(0)); + assertEquals(JA_STRING, lines.get(1)); // a sample of malformed sequences testReadAllLinesMalformedUTF8((byte)0xFF); // one-byte sequence @@ -284,57 +295,55 @@ public class BytesAndLines { ByteBuffer bb = UTF_8.newEncoder().encode(CharBuffer.wrap(s)); byte[] result = new byte[bb.limit()]; bb.get(result); - assertTrue(bb.remaining() == 0); + assertEquals(0, bb.remaining()); return result; } private void testReadAllLinesMalformedUTF8(byte... bytes) throws IOException { Files.write(tmpfile, bytes); - try { - Files.readAllLines(tmpfile); - fail("MalformedInputException expected"); - } catch (MalformedInputException ignore) { } + assertThrows(MalformedInputException.class, + () -> Files.readAllLines(tmpfile)); } /** * Exercise Files.write(Path, Iterable, Charset, OpenOption...) */ + @Test public void testWriteLines() throws IOException { // zero lines Path result = Files.write(tmpfile, Collections.emptyList(), US_ASCII); - assert(Files.size(tmpfile) == 0); - assert(result == tmpfile); + assertEquals(0, Files.size(tmpfile)); + assertSame(tmpfile, result); // two lines List lines = Arrays.asList("hi", "there"); Files.write(tmpfile, lines, US_ASCII); List actual = Files.readAllLines(tmpfile, US_ASCII); - assertTrue(actual.equals(lines), "Unexpected lines"); + assertLinesMatch(lines, actual, "Unexpected lines"); // append two lines Files.write(tmpfile, lines, US_ASCII, APPEND); List expected = new ArrayList<>(); expected.addAll(lines); expected.addAll(lines); - assertTrue(expected.size() == 4, "List should have 4 elements"); + assertEquals(4, expected.size()); actual = Files.readAllLines(tmpfile, US_ASCII); - assertTrue(actual.equals(expected), "Unexpected lines"); + assertLinesMatch(expected, actual, "Unexpected lines"); // UnmappableCharacterException - try { - String s = "\u00A0\u00A1"; - Files.write(tmpfile, Arrays.asList(s), US_ASCII); - fail("UnmappableCharacterException expected"); - } catch (UnmappableCharacterException ignore) { } + String s = "\u00A0\u00A1"; + assertThrows(UnmappableCharacterException.class, + () -> Files.write(tmpfile, Arrays.asList(s), US_ASCII)); } /** * Exercise Files.write(Path, Iterable, OpenOption...) */ + @Test public void testWriteLinesUTF8() throws IOException { List lines = Arrays.asList(EN_STRING, JA_STRING); Files.write(tmpfile, lines); List actual = Files.readAllLines(tmpfile, UTF_8); - assertTrue(actual.equals(lines), "Unexpected lines"); + assertLinesMatch(lines, actual, "Unexpected lines"); } } diff --git a/test/jdk/java/nio/file/Files/CallWithInterruptSet.java b/test/jdk/java/nio/file/Files/CallWithInterruptSet.java index 3d7bfd2c518..59d6a6a7ecb 100644 --- a/test/jdk/java/nio/file/Files/CallWithInterruptSet.java +++ b/test/jdk/java/nio/file/Files/CallWithInterruptSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -24,22 +24,24 @@ /* * @test * @bug 8205612 - * @run testng CallWithInterruptSet + * @run junit CallWithInterruptSet * @summary Test invoking Files methods with the interrupted status set */ -import java.io.InputStream; -import java.io.OutputStream; -import java.io.IOException; import java.io.BufferedReader; import java.io.BufferedWriter; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.nio.file.Files; import java.nio.file.Path; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class CallWithInterruptSet { @@ -49,7 +51,7 @@ public class CallWithInterruptSet { Thread.currentThread().interrupt(); try { byte[] bytes = Files.readAllBytes(file); - assertTrue(bytes.length == 100); + assertEquals(100, bytes.length); } finally { assertTrue(Thread.interrupted()); // clear interrupt } @@ -76,7 +78,7 @@ public class CallWithInterruptSet { } finally { assertTrue(Thread.interrupted()); // clear interrupt } - assertTrue(Files.size(file) == 10); + assertEquals(10, Files.size(file)); } @Test @@ -86,7 +88,7 @@ public class CallWithInterruptSet { Thread.currentThread().interrupt(); try { String msg = Files.readString(file); - assertEquals(msg, "hello"); + assertEquals("hello", msg); } finally { assertTrue(Thread.interrupted()); // clear interrupt } @@ -102,7 +104,7 @@ public class CallWithInterruptSet { assertTrue(Thread.interrupted()); // clear interrupt } String msg = Files.readString(file); - assertEquals(msg, "hello"); + assertEquals("hello", msg); } @Test @@ -112,7 +114,7 @@ public class CallWithInterruptSet { Thread.currentThread().interrupt(); try (BufferedReader reader = Files.newBufferedReader(file)) { String msg = reader.readLine(); - assertEquals(msg, "hello"); + assertEquals("hello", msg); } finally { assertTrue(Thread.interrupted()); // clear interrupt } @@ -128,7 +130,7 @@ public class CallWithInterruptSet { assertTrue(Thread.interrupted()); // clear interrupt } String msg = Files.readString(file); - assertEquals(msg, "hello"); + assertEquals("hello", msg); } private Path mkfile() throws IOException { diff --git a/test/jdk/java/nio/file/Files/CopyProcFile.java b/test/jdk/java/nio/file/Files/CopyProcFile.java index 892ac6b0eb4..c187aaed861 100644 --- a/test/jdk/java/nio/file/Files/CopyProcFile.java +++ b/test/jdk/java/nio/file/Files/CopyProcFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -33,20 +33,23 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.function.ToLongBiFunction; +import java.util.stream.Stream; import static java.nio.file.StandardOpenOption.*; -import org.testng.Assert; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.*; /* * @test * @bug 8293502 * @requires (os.family == "linux") * @summary Ensure that copying from a file in /proc works - * @run testng/othervm CopyProcFile + * @run junit/othervm CopyProcFile */ public class CopyProcFile { static final String SOURCE = "/proc/version"; @@ -112,8 +115,8 @@ public class CopyProcFile { } } - @BeforeTest(alwaysRun=true) - public void createBufferedCopy() throws IOException { + @BeforeAll + public static void createBufferedCopy() throws IOException { System.out.printf("Using source file \"%s\"%n", SOURCE); try { theSize = bufferedCopy(SOURCE, BUFFERED_COPY); @@ -129,8 +132,8 @@ public class CopyProcFile { } } - @AfterTest(alwaysRun=true) - public void deleteBufferedCopy() { + @AfterAll + public static void deleteBufferedCopy() { try { Files.delete(Path.of(BUFFERED_COPY)); } catch (IOException ignore) {} @@ -148,18 +151,17 @@ public class CopyProcFile { } } - @DataProvider - static Object[][] functions() throws IOException { - List funcs = new ArrayList<>(); - funcs.add(new Object[] {new FHolder((s, d) -> copy(s, d))}); - funcs.add(new Object[] {new FHolder((s, d) -> transferToIO(s, d))}); - funcs.add(new Object[] {new FHolder((s, d) -> transferToNIO(s, d))}); - funcs.add(new Object[] {new FHolder((s, d) -> transferFrom(s, d))}); - return funcs.toArray(Object[][]::new); + static Stream functions() throws IOException { + return Stream.of + (new FHolder((s, d) -> copy(s, d)), + new FHolder((s, d) -> transferToIO(s, d)), + new FHolder((s, d) -> transferToNIO(s, d)), + new FHolder((s, d) -> transferFrom(s, d))); } - @Test(dataProvider = "functions") - public static void testCopyAndTransfer(FHolder f) throws IOException { + @ParameterizedTest + @MethodSource("functions") + public void testCopyAndTransfer(FHolder f) throws IOException { try { long size = f.apply(SOURCE, TARGET); if (size != theSize) { @@ -168,9 +170,7 @@ public class CopyProcFile { } long mismatch = Files.mismatch(Path.of(BUFFERED_COPY), Path.of(TARGET)); - if (mismatch != -1) { - throw new RuntimeException("Target does not match copy"); - } + assertEquals(-1, mismatch, "Target does not match copy"); } finally { try { Files.delete(Path.of(TARGET)); diff --git a/test/jdk/java/nio/file/Files/CreateDirectories.java b/test/jdk/java/nio/file/Files/CreateDirectories.java index fc5a5025955..9912b442cfd 100644 --- a/test/jdk/java/nio/file/Files/CreateDirectories.java +++ b/test/jdk/java/nio/file/Files/CreateDirectories.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -26,16 +26,18 @@ import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; import java.nio.file.Path; -import static org.testng.Assert.*; -import org.testng.SkipException; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; + +import org.junit.jupiter.api.Assumptions; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @bug 8032220 8293792 8307976 * @summary Test java.nio.file.Files.createDirectories method * @library .. - * @run testng CreateDirectories + * @run junit CreateDirectories */ public class CreateDirectories { @@ -46,12 +48,10 @@ public class CreateDirectories { public void testSymlinkDir() throws Exception { // create a temp dir as the "root" in which we will run our tests. final Path top = TestUtil.createTemporaryDirectory(); - if (!TestUtil.supportsSymbolicLinks(top)) { - System.out.println("Skipping tests since symbolic links isn't " + - "supported under directory "+ top); - throw new SkipException("Symbolic links not supported"); - } - System.out.println("Running tests under directory " + top.toAbsolutePath()); + Assumptions.assumeTrue(TestUtil.supportsSymbolicLinks(top), + "Skipping as symbolic links are not supported under in " + top); + System.err.println("Running tests under directory " + + top.toAbsolutePath()); final Path fooDir = Files.createDirectory(top.resolve("foo")); assertTrue(Files.isDirectory(fooDir), fooDir + " was expected to be a directory but wasn't"); diff --git a/test/jdk/java/nio/file/Files/Mismatch.java b/test/jdk/java/nio/file/Files/Mismatch.java index 8746f448ad0..0d0446ed96e 100644 --- a/test/jdk/java/nio/file/Files/Mismatch.java +++ b/test/jdk/java/nio/file/Files/Mismatch.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -21,12 +21,6 @@ * questions. */ -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -36,13 +30,23 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.spi.FileSystemProvider; import java.util.Map; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + /* @test * @bug 8202285 * @build Mismatch - * @run testng Mismatch + * @run junit Mismatch * @summary Unit test for the Files.mismatch method. */ public class Mismatch { @@ -55,29 +59,28 @@ public class Mismatch { private static final Map ZIPFS_MAP = Map.of("create", "true"); // temporary test directory where all test files will be created - Path testDir; + static Path testDir; - @BeforeClass - void setup() throws IOException { + @BeforeAll + static void setup() throws IOException { testDir = Files.createTempDirectory("testMismatch"); } - @AfterClass - void cleanup() throws IOException { + @AfterAll + static void cleanup() throws IOException { // clean up files created under the test directory Files.walk(testDir).map(Path::toFile).forEach(File::delete); Files.deleteIfExists(testDir); } /* - * DataProvider for mismatch test. Provides the following fields: + * MethodSource for mismatch test. Provides the following fields: * path1 -- the path to a file * path2 -- the path to another file * expected -- expected result of the mismatch method * note -- a note about the test */ - @DataProvider(name = "testMismatch") - public Object[][] getDataForMismatch() throws IOException { + public static Stream getDataForMismatch() throws IOException { // an non-existent file Path foo = Paths.get("nonexistentfile"); @@ -147,151 +150,148 @@ public class Mismatch { Path test1065000m532500 = createASCIIFile(testDir, "test1065000m532500", size, size >> 1, '%'); Path test1065000m1064999 = createASCIIFile(testDir, "test1065000m1064999", size, 1064999, '$'); - return new Object[][]{ - // Spec Case 1: the two paths locate the same file , even if one does not exist - {foo, foo, MISMATCH_NO, "Same file, no mismatch"}, - {test1024a, test1024a, MISMATCH_NO, "Same file, no mismatch"}, + return Stream.of + (// Spec Case 1: the two paths locate the same file , even if one does not exist + Arguments.of(foo, foo, MISMATCH_NO, "Same file, no mismatch"), + Arguments.of(test1024a, test1024a, MISMATCH_NO, "Same file, no mismatch"), - // Spec Case 2: The two files are the same size, and every byte in the first file - // is identical to the corresponding byte in the second file. - {test0a, test0b, MISMATCH_NO, "Sizes == 0, no mismatch"}, - {test147a, test147b, MISMATCH_NO, "size = 147 < buffer = 8192, no mismatch"}, - {test1024a, test1024b, MISMATCH_NO, "size = 1024 < buffer = 8192, no mismatch"}, - {test8192a, test8192b, MISMATCH_NO, "size = 8192 = buffer = 8192, no mismatch"}, - {test65536a, test65536b, MISMATCH_NO, "read 8 * full buffer, no mismatch"}, - {test70025a, test70025b, MISMATCH_NO, "read 8 * full buffer plus a partial buffer, no mismatch"}, + // Spec Case 2: The two files are the same size, and every byte in the first file + // is identical to the corresponding byte in the second file. + Arguments.of(test0a, test0b, MISMATCH_NO, "Sizes == 0, no mismatch"), + Arguments.of(test147a, test147b, MISMATCH_NO, "size = 147 < buffer = 8192, no mismatch"), + Arguments.of(test1024a, test1024b, MISMATCH_NO, "size = 1024 < buffer = 8192, no mismatch"), + Arguments.of(test8192a, test8192b, MISMATCH_NO, "size = 8192 = buffer = 8192, no mismatch"), + Arguments.of(test65536a, test65536b, MISMATCH_NO, "read 8 * full buffer, no mismatch"), + Arguments.of(test70025a, test70025b, MISMATCH_NO, "read 8 * full buffer plus a partial buffer, no mismatch"), - /** - * Spec Case 3: the value returned is the position of the first mismatched byte - * Impl: the impl uses a buffer 8192. The testcases below covers a range of files - * with sizes <= and > the buffer size. The last buffer is either full or partially full. - */ + /* + * Spec Case 3: the value returned is the position of the first mismatched byte + * Impl: the impl uses a buffer 8192. The testcases below covers a range of files + * with sizes <= and > the buffer size. The last buffer is either full or partially full. + */ - // edge case, one of the file sizes is zero - // also covers Spec Case 4 and 6 - {test147a, test147m0, 0, "mismatch = 0 (at the beginning)"}, - {test65536m0, test65536a, 0, "mismatch = 0 (at the beginning)"}, + // edge case, one of the file sizes is zero + // also covers Spec Case 4 and 6 + Arguments.of(test147a, test147m0, 0, "mismatch = 0 (at the beginning)"), + Arguments.of(test65536m0, test65536a, 0, "mismatch = 0 (at the beginning)"), - /** - * Compares files of equal sizes - */ - // small files - {test147a, test147m70, 70, "read one partial buffer, mismatch = 70"}, - {test147a, test147m146, 146, "read one partial buffer, mismatch = 146 (end)"}, - {test1024a, test1024m512, 512, "read one partial buffer, mismatch = 512"}, - {test1024a, test1024m1023, 1023, "read one partial buffer, mismatch = 1023 (end)"}, + /* + * Compares files of equal sizes + */ + // small files + Arguments.of(test147a, test147m70, 70, "read one partial buffer, mismatch = 70"), + Arguments.of(test147a, test147m146, 146, "read one partial buffer, mismatch = 146 (end)"), + Arguments.of(test1024a, test1024m512, 512, "read one partial buffer, mismatch = 512"), + Arguments.of(test1024a, test1024m1023, 1023, "read one partial buffer, mismatch = 1023 (end)"), - // file size >= Impl's Buffer Size - {test8192a, test8192m4096, 4096, "read one buffer, mismatch = 4096 "}, - {test8192a, test8192m8191, 8191, "read one buffer, mismatch = 8191 (at the end)"}, + // file size >= Impl's Buffer Size + Arguments.of(test8192a, test8192m4096, 4096, "read one buffer, mismatch = 4096 "), + Arguments.of(test8192a, test8192m8191, 8191, "read one buffer, mismatch = 8191 (at the end)"), - // file size = n * Impl's Buffer Size - {test65536a, test65536m32768, 32768, "read through half of the file, mismatch = 32768"}, - {test65536a, test65536m65535, 65535, "read through the whole file, mismatch = 65535 (at the end)"}, + // file size = n * Impl's Buffer Size + Arguments.of(test65536a, test65536m32768, 32768, "read through half of the file, mismatch = 32768"), + Arguments.of(test65536a, test65536m65535, 65535, "read through the whole file, mismatch = 65535 (at the end)"), - // file size = n * Impl's Buffer Size + x - {test70025a, test70025m8400, 8400, "mismatch in the 2nd buffer, mismatch = 8400"}, - {test70025a, test70025m35000, 35000, "read about half of the file, mismatch = 35000"}, - {test70025a, test70025m70024, 70024, "read through the whole file, mismatch = 70024 (at the end)"}, + // file size = n * Impl's Buffer Size + x + Arguments.of(test70025a, test70025m8400, 8400, "mismatch in the 2nd buffer, mismatch = 8400"), + Arguments.of(test70025a, test70025m35000, 35000, "read about half of the file, mismatch = 35000"), + Arguments.of(test70025a, test70025m70024, 70024, "read through the whole file, mismatch = 70024 (at the end)"), - /** - * Compares files of unequal sizes - */ - {test8192m8191, test70025m35000, 8191, "mismatch at the end of the 1st file/buffer, mismatch = 8191"}, - {test65536m32768, test70025m8400, 8400, "mismatch in the 2nd buffer, mismatch = 8400"}, - {test70025m70024, test1065000m532500, 70024, "mismatch at the end of the 1st file, mismatch = 70024"}, + /* + * Compares files of unequal sizes + */ + Arguments.of(test8192m8191, test70025m35000, 8191, "mismatch at the end of the 1st file/buffer, mismatch = 8191"), + Arguments.of(test65536m32768, test70025m8400, 8400, "mismatch in the 2nd buffer, mismatch = 8400"), + Arguments.of(test70025m70024, test1065000m532500, 70024, "mismatch at the end of the 1st file, mismatch = 70024"), - /** - * Spec Case 4: returns the size of the smaller file (in bytes) when the files are - * different sizes and every byte of the smaller file is identical to the corresponding - * byte of the larger file. - * Impl: similar to case 3, covers a range of file sizes - */ - {test147a, test1024a, 147, "mismatch is the length of the smaller file: 147"}, - {test1024a, test8192a, 1024, "mismatch is the length of the smaller file: 1024"}, - {test1024a, test65536a, 1024, "mismatch is the length of the smaller file: 1024"}, - {test8192a, test65536a, 8192, "mismatch is the length of the smaller file: 8192"}, - {test70025a, test65536a, 65536, "mismatch is the length of the smaller file: 65536"}, - {test1048576a, test1065000m1064999, 1048576, "mismatch is the length of the smaller file: 1048576"}, + /* + * Spec Case 4: returns the size of the smaller file (in bytes) when the files are + * different sizes and every byte of the smaller file is identical to the corresponding + * byte of the larger file. + * Impl: similar to case 3, covers a range of file sizes + */ + Arguments.of(test147a, test1024a, 147, "mismatch is the length of the smaller file: 147"), + Arguments.of(test1024a, test8192a, 1024, "mismatch is the length of the smaller file: 1024"), + Arguments.of(test1024a, test65536a, 1024, "mismatch is the length of the smaller file: 1024"), + Arguments.of(test8192a, test65536a, 8192, "mismatch is the length of the smaller file: 8192"), + Arguments.of(test70025a, test65536a, 65536, "mismatch is the length of the smaller file: 65536"), + Arguments.of(test1048576a, test1065000m1064999, 1048576, "mismatch is the length of the smaller file: 1048576"), - // Spec Case 5: This method is always reflexive (for Path f , mismatch(f,f) returns -1L) - // See tests for Spec Case 1. + // Spec Case 5: This method is always reflexive (for Path f , mismatch(f,f) returns -1L) + // See tests for Spec Case 1. - // Spec Case 6: If the file system and files remain static, then this method is symmetric - // (for two Paths f and g, mismatch(f,g) will return the same value as mismatch(g,f)). - // The following tests are selected from tests for Spec Case 3 with the order of - // file paths switched, the returned values are the same as those for Case 3: - {test147m70, test147a, 70, "read one partial buffer, mismatch = 70"}, - {test147m146, test147a, 146, "read one partial buffer, mismatch = 146 (end)"}, - {test1024m512, test1024a, 512, "read one partial buffer, mismatch = 512"}, - {test1024m1023, test1024a, 1023, "read one partial buffer, mismatch = 1023 (end)"}, + // Spec Case 6: If the file system and files remain static, then this method is symmetric + // (for two Paths f and g, mismatch(f,g) will return the same value as mismatch(g,f)). + // The following tests are selected from tests for Spec Case 3 with the order of + // file paths switched, the returned values are the same as those for Case 3: + Arguments.of(test147m70, test147a, 70, "read one partial buffer, mismatch = 70"), + Arguments.of(test147m146, test147a, 146, "read one partial buffer, mismatch = 146 (end)"), + Arguments.of(test1024m512, test1024a, 512, "read one partial buffer, mismatch = 512"), + Arguments.of(test1024m1023, test1024a, 1023, "read one partial buffer, mismatch = 1023 (end)"), - {test70025m35000, test8192m8191, 8191, "mismatch at the end of the 1st file/buffer, mismatch = 8191"}, - {test70025m8400, test65536m32768, 8400, "mismatch in the 2nd buffer, mismatch = 8400"}, - {test1065000m532500, test70025m70024, 70024, "mismatch at the end of the 1st file, mismatch = 70024"}, - }; + Arguments.of(test70025m35000, test8192m8191, 8191, "mismatch at the end of the 1st file/buffer, mismatch = 8191"), + Arguments.of(test70025m8400, test65536m32768, 8400, "mismatch in the 2nd buffer, mismatch = 8400"), + Arguments.of(test1065000m532500, test70025m70024, 70024, "mismatch at the end of the 1st file, mismatch = 70024") + ); } /* - * DataProvider for mismatch tests involving ZipFS using a few test cases selected + * MethodSource for mismatch tests involving ZipFS using a few test cases selected * from those of the original mismatch tests. */ - @DataProvider(name = "testMismatchZipfs") - public Object[][] getDataForMismatchZipfs() throws IOException { + public static Stream getDataForMismatchZipfs() throws IOException { Path test1200 = createASCIIFile(testDir, "test1200", 1200, -1, ' '); Path test9500 = createASCIIFile(testDir, "test9500", 9500, -1, ' '); Path test9500m4200 = createASCIIFile(testDir, "test9500m4200", 9500, 4200, '!'); Path test80025 = createASCIIFile(testDir, "test80025", 80025, -1, ' '); Path test1028500 = createASCIIFile(testDir, "test1028500", 1028500, -1, ' '); - return new Object[][]{ - {test1200, test1200, MISMATCH_NO, "Compares the file and its copy in zip, no mismatch"}, - {test9500, test9500m4200, 4200, - "Compares a copy of test9500m4200 in zip with test9500, shall return 4200"}, - {test80025, test1028500, 80025, "mismatch is the length of the smaller file: 80025"}, - }; + return Stream.of + ( + Arguments.of(test1200, test1200, MISMATCH_NO, "Compares the file and its copy in zip, no mismatch"), + Arguments.of(test9500, test9500m4200, 4200, + "Compares a copy of test9500m4200 in zip with test9500, shall return 4200"), + Arguments.of(test80025, test1028500, 80025, + "mismatch is the length of the smaller file: 80025") + ); } /* - * DataProvider for verifying null handling. + * MethodSource for verifying null handling. */ - @DataProvider(name = "testFileNull") - public Object[][] getDataForNull() throws IOException { + public static Stream getDataForNull() throws IOException { Path test = createASCIIFile(testDir, "testNonNull", 2200, -1, ' '); - return new Object[][]{ - {(Path)null, (Path)null}, - {(Path)null, test}, - {test, (Path)null}, - }; + return Stream.of(Arguments.of((Path)null, (Path)null), + Arguments.of((Path)null, test), + Arguments.of(test, (Path)null)); } /* - * DataProvider for verifying how the mismatch method handles the situation + * MethodSource for verifying how the mismatch method handles the situation * when one or both files do not exist. */ - @DataProvider(name = "testFileNotExist") - public Object[][] getDataForFileNotExist() throws IOException { + public static Stream getDataForFileNotExist() throws IOException { Path test = createASCIIFile(testDir, "testFileNotExist", 3200, -1, ' '); - return new Object[][]{ - {Paths.get("foo"), Paths.get("bar")}, - {Paths.get("foo"), test}, - {test, Paths.get("bar")}, - }; + return Stream.of + (Arguments.of(Paths.get("foo"), Paths.get("bar")), + Arguments.of("foo", test), + Arguments.of(test, Paths.get("bar"))); } /** - * Tests the mismatch method. Refer to the dataProvider testMismatch for more - * details about the cases. + * Tests the mismatch method. Refer to the dataProvider getDataForNull for + * more details about the cases. * @param path the path to a file * @param path2 the path to another file * @param expected the expected result * @param msg the message about the test * @throws IOException if the test fails */ - @Test(dataProvider = "testMismatch", priority = 0) + @ParameterizedTest + @MethodSource("getDataForMismatch") public void testMismatch(Path path, Path path2, long expected, String msg) throws IOException { - Assert.assertEquals(Files.mismatch(path, path2), expected, msg); + assertEquals(expected, Files.mismatch(path, path2), msg); } /** @@ -302,7 +302,8 @@ public class Mismatch { * @param msg the message about the test * @throws IOException if the test fails */ - @Test(dataProvider = "testMismatchZipfs", priority = 1) + @ParameterizedTest + @MethodSource("getDataForMismatchZipfs") public void testMismatchZipfs(Path path, Path path2, long expected, String msg) throws IOException { Path zipPath = Paths.get(testDir.toString(), "TestWithFSZip.zip"); @@ -311,9 +312,9 @@ public class Mismatch { Files.copy(path, copy, REPLACE_EXISTING); if (path2 == null) { - Assert.assertEquals(Files.mismatch(copy, path), expected, msg); + assertEquals(expected, Files.mismatch(copy, path), msg); } else { - Assert.assertEquals(Files.mismatch(copy, path2), expected, msg); + assertEquals(expected, Files.mismatch(copy, path2), msg); } } } @@ -324,9 +325,11 @@ public class Mismatch { * @param path2 the path to another file * @throws NullPointerException as expected */ - @Test(dataProvider = "testFileNull", priority = 2, expectedExceptions = NullPointerException.class) + @ParameterizedTest + @MethodSource("getDataForNull") public void testMismatchNull(Path path, Path path2) throws Exception { - long result = Files.mismatch(path, path2); + assertThrows(NullPointerException.class, + () -> Files.mismatch(path, path2)); } /** @@ -335,9 +338,11 @@ public class Mismatch { * @param path2 the path to another file * @throws IOException as expected */ - @Test(dataProvider = "testFileNotExist", priority = 2, expectedExceptions = IOException.class) + @ParameterizedTest + @MethodSource("getDataForFileNotExist") public void testMismatchNotExist(Path path, Path path2) throws IOException { - long result = Files.mismatch(path, path2); + assertThrows(IOException.class, + () -> {long result = Files.mismatch(path, path2);}); } /** diff --git a/test/jdk/java/nio/file/Files/ReadWriteString.java b/test/jdk/java/nio/file/Files/ReadWriteString.java index 8b5241fa1cf..3510f41e979 100644 --- a/test/jdk/java/nio/file/Files/ReadWriteString.java +++ b/test/jdk/java/nio/file/Files/ReadWriteString.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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,6 +27,21 @@ import java.nio.charset.Charset; import java.nio.charset.CharacterCodingException; import java.nio.charset.MalformedInputException; import java.nio.charset.UnmappableCharacterException; +import java.nio.file.Files; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Random; +import java.util.concurrent.Callable; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import static java.nio.charset.StandardCharsets.ISO_8859_1; import static java.nio.charset.StandardCharsets.US_ASCII; import static java.nio.charset.StandardCharsets.UTF_8; @@ -36,36 +51,31 @@ import static java.nio.charset.StandardCharsets.UTF_16LE; import static java.nio.charset.StandardCharsets.UTF_32; import static java.nio.charset.StandardCharsets.UTF_32BE; import static java.nio.charset.StandardCharsets.UTF_32LE; -import java.nio.file.Files; -import java.nio.file.OpenOption; -import java.nio.file.Path; -import java.nio.file.Paths; + import static java.nio.file.StandardOpenOption.APPEND; import static java.nio.file.StandardOpenOption.CREATE; -import java.util.Arrays; -import java.util.Random; -import java.util.concurrent.Callable; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /* @test * @bug 8201276 8205058 8209576 8287541 8288589 8325590 * @build ReadWriteString PassThroughFileSystem - * @run testng ReadWriteString + * @run junit ReadWriteString * @summary Unit test for methods for Files readString and write methods. * @key randomness * @modules jdk.charsets */ -@Test(groups = "readwrite") public class ReadWriteString { // data for text files - final String TEXT_UNICODE = "\u201CHello\u201D"; - final String TEXT_ASCII = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\n abcdefghijklmnopqrstuvwxyz\n 1234567890\n"; + final static String TEXT_UNICODE = "\u201CHello\u201D"; + final static String TEXT_ASCII = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\n abcdefghijklmnopqrstuvwxyz\n 1234567890\n"; final static String TEXT_PERSON_CART_WHEELING = "\ud83e\udd38"; private static final String JA_STRING = "\u65e5\u672c\u8a9e\u6587\u5b57\u5217"; private static final Charset WINDOWS_1252 = Charset.forName("windows-1252"); @@ -85,96 +95,85 @@ public class ReadWriteString { return baos.toByteArray(); } catch (IOException ex) { // in case it happens, fail the test - throw new RuntimeException(ex); + fail(ex); + return null; // appease the compiler } } // file used by testReadWrite, testReadString and testWriteString - private Path[] testFiles = new Path[3]; + private static Path[] testFiles = new Path[3]; /* - * DataProvider for malformed write test. Provides the following fields: + * MethodSource for malformed write test. Provides the following fields: * file path, malformed input string, charset */ - @DataProvider(name = "malformedWrite") - public Object[][] getMalformedWrite() throws IOException { + public static Stream getMalformedWrite() throws IOException { Path path = Files.createFile(Path.of("malformedWrite")); - return new Object[][]{ - {path, "\ud800", null}, //the default Charset is UTF_8 - {path, "\u00A0\u00A1", US_ASCII}, - {path, "\ud800", UTF_8}, - {path, JA_STRING, ISO_8859_1}, - {path, "\u041e", WINDOWS_1252}, // cyrillic capital letter O - {path, "\u091c", WINDOWS_31J}, // devanagari letter ja - }; + return Stream.of + (Arguments.of(path, "\ud800", null), //the default Charset is UTF_8 + Arguments.of(path, "\u00A0\u00A1", US_ASCII), + Arguments.of(path, "\ud800", UTF_8), + Arguments.of(path, JA_STRING, ISO_8859_1), + Arguments.of(path, "\u041e", WINDOWS_1252), // cyrillic capital letter O + Arguments.of(path, "\u091c", WINDOWS_31J)); // devanagari letter ja } /* - * DataProvider for illegal input test + * MethodSource for illegal input test * Writes the data in ISO8859 and reads with UTF_8, expects MalformedInputException */ - @DataProvider(name = "illegalInput") - public Object[][] getIllegalInput() throws IOException { + public static Stream getIllegalInput() throws IOException { Path path = Files.createFile(Path.of("illegalInput")); - return new Object[][]{ - {path, data, ISO_8859_1, null}, - {path, data, ISO_8859_1, UTF_8} - }; + return Stream.of(Arguments.of(path, data, ISO_8859_1, null), + Arguments.of(path, data, ISO_8859_1, UTF_8)); } /* - * DataProvider for illegal input bytes test + * MethodSource for illegal input bytes test */ - @DataProvider(name = "illegalInputBytes") - public Object[][] getIllegalInputBytes() throws IOException { - return new Object[][]{ - {new byte[] {(byte)0x00, (byte)0x20, (byte)0x00}, UTF_16, MalformedInputException.class}, - {new byte[] {-50}, UTF_16, MalformedInputException.class}, - {new byte[] {(byte)0x81}, WINDOWS_1252, UnmappableCharacterException.class}, // unused in Cp1252 - {new byte[] {(byte)0x81, (byte)0xff}, WINDOWS_31J, UnmappableCharacterException.class}, // invalid trailing byte - }; + public static Stream getIllegalInputBytes() throws IOException { + return Stream.of + (Arguments.of(new byte[] {(byte)0x00, (byte)0x20, (byte)0x00}, UTF_16, MalformedInputException.class), + Arguments.of(new byte[] {-50}, UTF_16, MalformedInputException.class), + Arguments.of(new byte[] {(byte)0x81}, WINDOWS_1252, UnmappableCharacterException.class), // unused in Cp1252 + Arguments.of(new byte[] {(byte)0x81, (byte)0xff}, WINDOWS_31J, UnmappableCharacterException.class)); // invalid trailing byte } /* - * DataProvider for writeString test + * MethodSource for writeString test * Writes the data using both the existing and new method and compares the results. */ - @DataProvider(name = "testWriteString") - public Object[][] getWriteString() { - - return new Object[][]{ - {testFiles[1], testFiles[2], TEXT_ASCII, US_ASCII, null}, - {testFiles[1], testFiles[2], TEXT_ASCII, US_ASCII, US_ASCII}, - {testFiles[1], testFiles[2], TEXT_UNICODE, UTF_8, null}, - {testFiles[1], testFiles[2], TEXT_UNICODE, UTF_8, UTF_8} - }; + public static Stream getWriteString() { + return Stream.of + (Arguments.of(testFiles[1], testFiles[2], TEXT_ASCII, US_ASCII, null), + Arguments.of(testFiles[1], testFiles[2], TEXT_ASCII, US_ASCII, US_ASCII), + Arguments.of(testFiles[1], testFiles[2], TEXT_UNICODE, UTF_8, null), + Arguments.of(testFiles[1], testFiles[2], TEXT_UNICODE, UTF_8, UTF_8)); } /* - * DataProvider for readString test + * MethodSource for readString test * Reads the file using both the existing and new method and compares the results. */ - @DataProvider(name = "testReadString") - public Object[][] getReadString() { - return new Object[][]{ - {testFiles[1], TEXT_ASCII, US_ASCII, US_ASCII}, - {testFiles[1], TEXT_ASCII, US_ASCII, UTF_8}, - {testFiles[1], TEXT_UNICODE, UTF_8, null}, - {testFiles[1], TEXT_UNICODE, UTF_8, UTF_8}, - {testFiles[1], TEXT_ASCII, US_ASCII, ISO_8859_1}, - {testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_16, UTF_16}, - {testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_16BE, UTF_16BE}, - {testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_16LE, UTF_16LE}, - {testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_32, UTF_32}, - {testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_32BE, UTF_32BE}, - {testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_32LE, UTF_32LE}, - {testFiles[1], TEXT_PERSON_CART_WHEELING, WINDOWS_1252, WINDOWS_1252}, - {testFiles[1], TEXT_PERSON_CART_WHEELING, WINDOWS_31J, WINDOWS_31J} - }; + public static Stream getReadString() { + return Stream.of + (Arguments.of(testFiles[1], TEXT_ASCII, US_ASCII, US_ASCII), + Arguments.of(testFiles[1], TEXT_ASCII, US_ASCII, UTF_8), + Arguments.of(testFiles[1], TEXT_UNICODE, UTF_8, null), + Arguments.of(testFiles[1], TEXT_UNICODE, UTF_8, UTF_8), + Arguments.of(testFiles[1], TEXT_ASCII, US_ASCII, ISO_8859_1), + Arguments.of(testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_16, UTF_16), + Arguments.of(testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_16BE, UTF_16BE), + Arguments.of(testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_16LE, UTF_16LE), + Arguments.of(testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_32, UTF_32), + Arguments.of(testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_32BE, UTF_32BE), + Arguments.of(testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_32LE, UTF_32LE), + Arguments.of(testFiles[1], TEXT_PERSON_CART_WHEELING, WINDOWS_1252, WINDOWS_1252), + Arguments.of(testFiles[1], TEXT_PERSON_CART_WHEELING, WINDOWS_31J, WINDOWS_31J)); } - @BeforeClass - void setup() throws IOException { + @BeforeAll + static void setup() throws IOException { testFiles[0] = Files.createFile(Path.of("readWriteString")); testFiles[1] = Files.createFile(Path.of("writeString_file1")); testFiles[2] = Files.createFile(Path.of("writeString_file2")); @@ -225,7 +224,8 @@ public class ReadWriteString { * This method compares the results written by the existing write method and * the writeString method added since 11. */ - @Test(dataProvider = "testWriteString") + @ParameterizedTest + @MethodSource("getWriteString") public void testWriteString(Path path, Path path2, String text, Charset cs, Charset cs2) throws IOException { Files.write(path, text.getBytes(cs)); @@ -237,14 +237,15 @@ public class ReadWriteString { } byte[] bytes = Files.readAllBytes(path); byte[] bytes2 = Files.readAllBytes(path2); - assertTrue((Arrays.compare(bytes, bytes2) == 0), "The bytes should be the same"); + assertArrayEquals(bytes2, bytes, "The bytes should be the same"); } /** * Verifies that the readString method added since 11 behaves the same as * constructing a string from the existing readAllBytes method. */ - @Test(dataProvider = "testReadString") + @ParameterizedTest + @MethodSource("getReadString") public void testReadString(Path path, String text, Charset cs, Charset cs2) throws IOException { Files.write(path, text.getBytes(cs)); String str = new String(Files.readAllBytes(path), cs); @@ -252,7 +253,7 @@ public class ReadWriteString { // readString @since 11 String str2 = (cs2 == null) ? Files.readString(path) : Files.readString(path, cs2); - assertTrue((str.equals(str2)), "The strings should be the same"); + assertEquals(str, str2, "The strings should be the same"); } /** @@ -264,13 +265,17 @@ public class ReadWriteString { * @param cs the Charset * @throws IOException if the input is malformed */ - @Test(dataProvider = "malformedWrite", expectedExceptions = UnmappableCharacterException.class) + @ParameterizedTest + @MethodSource("getMalformedWrite") public void testMalformedWrite(Path path, String s, Charset cs) throws IOException { - if (cs == null) { - Files.writeString(path, s); - } else { - Files.writeString(path, s, cs); - } + assertThrows(UnmappableCharacterException.class, + () -> { + if (cs == null) { + Files.writeString(path, s); + } else { + Files.writeString(path, s, cs); + } + }); } /** @@ -283,15 +288,19 @@ public class ReadWriteString { * @param csRead the Charset to use for reading the file * @throws IOException when the Charset used for reading the file is incorrect */ - @Test(dataProvider = "illegalInput", expectedExceptions = MalformedInputException.class) + @ParameterizedTest + @MethodSource("getIllegalInput") public void testMalformedRead(Path path, byte[] data, Charset csWrite, Charset csRead) throws IOException { String temp = new String(data, csWrite); Files.writeString(path, temp, csWrite); - if (csRead == null) { - Files.readString(path); - } else { - Files.readString(path, csRead); - } + assertThrows(MalformedInputException.class, + () -> { + if (csRead == null) { + Files.readString(path); + } else { + Files.readString(path, csRead); + } + }); } /** @@ -303,20 +312,19 @@ public class ReadWriteString { * @param expected exception class * @throws IOException when the Charset used for reading the file is incorrect */ - @Test(dataProvider = "illegalInputBytes") + @ParameterizedTest + @MethodSource("getIllegalInputBytes") public void testMalformedReadBytes(byte[] data, Charset csRead, Class expected) throws IOException { Path path = Path.of("illegalInputBytes"); Files.write(path, data); try { Files.readString(path, csRead); - } catch (MalformedInputException | UnmappableCharacterException e) { - if (expected.isInstance(e)) { - // success - return; - } + } catch (MalformedInputException e) { + assertInstanceOf(MalformedInputException.class, e); + } catch (UnmappableCharacterException e) { + assertInstanceOf(UnmappableCharacterException.class, e); } - throw new RuntimeException("An instance of " + expected + " should be thrown"); } // Verify File.readString with UTF16 to confirm proper string length and contents. @@ -326,22 +334,16 @@ public class ReadWriteString { String original = "🤸"; // "\ud83e\udd38"; Files.writeString(testFiles[0], original, UTF_16); String actual = Files.readString(testFiles[0], UTF_16); - if (!actual.equals(original)) { + if (!original.equals(actual)) { System.out.printf("expected (%s), was (%s)\n", original, actual); System.out.printf("expected UTF_16 bytes: %s\n", Arrays.toString(original.getBytes(UTF_16))); System.out.printf("actual UTF_16 bytes: %s\n", Arrays.toString(actual.getBytes(UTF_16))); } - assertEquals(actual, original, "Round trip string mismatch with multi-byte encoding"); + assertEquals(original, actual, "Round trip string mismatch with multi-byte encoding"); } private void checkNullPointerException(Callable c) { - try { - c.call(); - fail("NullPointerException expected"); - } catch (NullPointerException ignore) { - } catch (Exception e) { - fail(e + " not expected"); - } + assertThrows(NullPointerException.class, () -> c.call()); } private void testReadWrite(int size, Charset cs, boolean append) throws IOException { @@ -355,14 +357,14 @@ public class ReadWriteString { } //System.out.println(result.toUri().toASCIIString()); - assertTrue(result == testFiles[0]); + assertSame(result, testFiles[0]); if (append) { if (cs == null) { Files.writeString(testFiles[0], str, APPEND); } else { Files.writeString(testFiles[0], str, cs, APPEND); } - assertTrue(Files.size(testFiles[0]) == size * 2); + assertEquals(size * 2, Files.size(testFiles[0])); } @@ -379,7 +381,7 @@ public class ReadWriteString { read = Files.readString(result, cs); } - assertTrue(read.equals(expected), "String read not the same as written"); + assertEquals(expected, read, "String read not the same as written"); } static final char[] CHARS = "abcdefghijklmnopqrstuvwxyz \r\n".toCharArray(); diff --git a/test/jdk/java/nio/file/Files/SetLastModifiedTime.java b/test/jdk/java/nio/file/Files/SetLastModifiedTime.java index d06b78b2922..c4e5547fb14 100644 --- a/test/jdk/java/nio/file/Files/SetLastModifiedTime.java +++ b/test/jdk/java/nio/file/Files/SetLastModifiedTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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,19 +27,20 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.FileTime; -import org.testng.annotations.Test; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @test * @bug 4313887 8062949 8191872 * @library .. - * @run testng SetLastModifiedTime + * @run junit SetLastModifiedTime * @summary Unit test for Files.setLastModifiedTime */ @@ -47,13 +48,13 @@ public class SetLastModifiedTime { static Path testDir; - @BeforeClass - void createTestDirectory() throws Exception { + @BeforeAll + static void createTestDirectory() throws Exception { testDir = TestUtil.createTemporaryDirectory(); } - @AfterClass - void removeTestDirectory() throws Exception { + @AfterAll + static void removeTestDirectory() throws Exception { TestUtil.removeAll(testDir); } @@ -65,12 +66,12 @@ public class SetLastModifiedTime { FileTime zero = FileTime.fromMillis(0L); Path result = Files.setLastModifiedTime(path, zero); - assertTrue(result == path); - assertEquals(Files.getLastModifiedTime(path), zero); + assertSame(path, result); + assertEquals(zero, Files.getLastModifiedTime(path)); result = Files.setLastModifiedTime(path, now); - assertTrue(result == path); - assertEquals(Files.getLastModifiedTime(path), now); + assertSame(path, result); + assertEquals(now, Files.getLastModifiedTime(path)); } @Test @@ -100,20 +101,14 @@ public class SetLastModifiedTime { Path path = Paths.get("foo"); FileTime zero = FileTime.fromMillis(0L); - try { - Files.setLastModifiedTime(null, zero); - assertTrue(false); - } catch (NullPointerException expected) { } + assertThrows(NullPointerException.class, + () -> Files.setLastModifiedTime(null, zero)); - try { - Files.setLastModifiedTime(path, null); - assertTrue(false); - } catch (NullPointerException expected) { } + assertThrows(NullPointerException.class, + () -> Files.setLastModifiedTime(path, null)); - try { - Files.setLastModifiedTime(null, null); - assertTrue(false); - } catch (NullPointerException expected) { } + assertThrows(NullPointerException.class, + () -> Files.setLastModifiedTime(null, null)); } @Test @@ -127,7 +122,7 @@ public class SetLastModifiedTime { long nioTime = Files.getLastModifiedTime(path).toMillis(); assertTrue(ioTime == timeMillis || ioTime == 1000*(timeMillis/1000), "File.lastModified() not in {time, 1000*(time/1000)}"); - assertEquals(nioTime, ioTime, + assertEquals(ioTime, nioTime, "File.lastModified() != Files.getLastModifiedTime().toMillis()"); } } diff --git a/test/jdk/java/nio/file/Files/StreamTest.java b/test/jdk/java/nio/file/Files/StreamTest.java index 0c93a622322..e9e6195ec0a 100644 --- a/test/jdk/java/nio/file/Files/StreamTest.java +++ b/test/jdk/java/nio/file/Files/StreamTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, 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 @@ -25,7 +25,7 @@ * @bug 8006884 8019526 8132539 * @library .. * @build PassThroughFileSystem FaultyFileSystem - * @run testng StreamTest + * @run junit StreamTest * @summary Unit test for java.nio.file.Files methods that return a Stream */ @@ -35,9 +35,9 @@ import java.nio.charset.Charset; import java.nio.charset.MalformedInputException; import java.nio.file.DirectoryIteratorException; import java.nio.file.DirectoryStream; +import java.nio.file.Files; import java.nio.file.FileSystemLoopException; import java.nio.file.FileVisitOption; -import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; @@ -51,14 +51,23 @@ import java.util.Set; import java.util.TreeSet; import java.util.concurrent.Callable; import java.util.function.BiPredicate; -import java.util.stream.Stream; import java.util.stream.Collectors; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; -@Test(groups = "unit") public class StreamTest { /** * Default test folder @@ -77,8 +86,8 @@ public class StreamTest { static Path[] all; static Path[] allFollowLinks; - @BeforeClass - void setupTestFolder() throws IOException { + @BeforeAll + static void setupTestFolder() throws IOException { testFolder = TestUtil.createTemporaryDirectory(); supportsSymbolicLinks = TestUtil.supportsSymbolicLinks(testFolder); TreeSet set = new TreeSet<>(); @@ -133,53 +142,57 @@ public class StreamTest { allFollowLinks = set.toArray(new Path[0]); } - @AfterClass - void cleanupTestFolder() throws IOException { + @AfterAll + static void cleanupTestFolder() throws IOException { TestUtil.removeAll(testFolder); } + @Test public void testBasic() { try (Stream s = Files.list(testFolder)) { Object[] actual = s.sorted().toArray(); - assertEquals(actual, level1); + assertArrayEquals(level1, actual); } catch (IOException ioe) { fail("Unexpected IOException"); } try (Stream s = Files.list(testFolder.resolve("empty"))) { int count = s.mapToInt(p -> 1).reduce(0, Integer::sum); - assertEquals(count, 0, "Expect empty stream."); + assertEquals(0, count, "Expect empty stream."); } catch (IOException ioe) { fail("Unexpected IOException"); } } + @Test public void testWalk() { try (Stream s = Files.walk(testFolder)) { Object[] actual = s.sorted().toArray(); - assertEquals(actual, all); + assertArrayEquals(all, actual); } catch (IOException ioe) { fail("Unexpected IOException"); } } + @Test public void testWalkOneLevel() { try (Stream s = Files.walk(testFolder, 1)) { Object[] actual = s.filter(path -> ! path.equals(testFolder)) .sorted() .toArray(); - assertEquals(actual, level1); + assertArrayEquals(level1, actual); } catch (IOException ioe) { fail("Unexpected IOException"); } } + @Test public void testWalkFollowLink() { // If link is not supported, the directory structure won't have link. // We still want to test the behavior with FOLLOW_LINKS option. try (Stream s = Files.walk(testFolder, FileVisitOption.FOLLOW_LINKS)) { Object[] actual = s.sorted().toArray(); - assertEquals(actual, allFollowLinks); + assertArrayEquals(allFollowLinks, actual); } catch (IOException ioe) { fail("Unexpected IOException"); } @@ -211,6 +224,7 @@ public class StreamTest { } } + @Test public void testWalkFollowLinkLoop() { if (!supportsSymbolicLinks) { return; @@ -257,6 +271,7 @@ public class StreamTest { dir2.resolve(Paths.get("lnDir", "lnDir2"))); validateFileSystemLoopException(linkdir, linkdir.resolve(Paths.get("lnDir2", "lnDir"))); + Files.delete(cause); } catch(IOException ioe) { fail("Unexpected IOException " + ioe); } @@ -280,44 +295,46 @@ public class StreamTest { } } + @Test public void testFind() throws IOException { PathBiPredicate pred = new PathBiPredicate((path, attrs) -> true); try (Stream s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { Set result = s.collect(Collectors.toCollection(TreeSet::new)); - assertEquals(pred.visited(), all); - assertEquals(result.toArray(new Path[0]), pred.visited()); + assertArrayEquals(all, pred.visited()); + assertArrayEquals(pred.visited(), result.toArray(new Path[0])); } pred = new PathBiPredicate((path, attrs) -> attrs.isSymbolicLink()); try (Stream s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { s.forEach(path -> assertTrue(Files.isSymbolicLink(path))); - assertEquals(pred.visited(), all); + assertArrayEquals(all, pred.visited()); } pred = new PathBiPredicate((path, attrs) -> path.getFileName().toString().startsWith("e")); try (Stream s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { s.forEach(path -> assertEquals(path.getFileName().toString(), "empty")); - assertEquals(pred.visited(), all); + assertArrayEquals(all, pred.visited()); } pred = new PathBiPredicate((path, attrs) -> path.getFileName().toString().startsWith("l") && attrs.isRegularFile()); try (Stream s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { s.forEach(path -> fail("Expect empty stream")); - assertEquals(pred.visited(), all); + assertArrayEquals(all, pred.visited()); } } // Test borrowed from BytesAndLines + @Test public void testLines() throws IOException { final Charset US_ASCII = Charset.forName("US-ASCII"); Path tmpfile = Files.createTempFile("blah", "txt"); try { // zero lines - assertTrue(Files.size(tmpfile) == 0, "File should be empty"); + assertEquals(0, Files.size(tmpfile), "File should be empty"); try (Stream s = Files.lines(tmpfile)) { checkLines(s, Collections.emptyList()); } @@ -367,31 +384,21 @@ public class StreamTest { private void checkLines(Stream s, List expected) { List lines = s.collect(Collectors.toList()); - assertTrue(lines.size() == expected.size(), "Unexpected number of lines"); + assertEquals(expected.size(), lines.size(), "Unexpected number of lines"); assertTrue(lines.equals(expected), "Unexpected content"); } private void checkMalformedInputException(Stream s) { - try { - List lines = s.collect(Collectors.toList()); - fail("UncheckedIOException expected"); - } catch (UncheckedIOException ex) { - IOException cause = ex.getCause(); - assertTrue(cause instanceof MalformedInputException, - "MalformedInputException expected"); - } + assertThrows(UncheckedIOException.class, + () -> s.collect(Collectors.toList()), + "MalformedInputException expected"); } private void checkNullPointerException(Callable c) { - try { - c.call(); - fail("NullPointerException expected"); - } catch (NullPointerException ignore) { - } catch (Exception e) { - fail(e + " not expected"); - } + assertThrows(NullPointerException.class, () -> c.call()); } + @Test public void testDirectoryIteratorException() throws IOException { Path dir = testFolder.resolve("dir2"); Path trigger = dir.resolve("DirectoryIteratorException"); @@ -404,22 +411,21 @@ public class StreamTest { Path fakeRoot = fs.getRoot(); try { try (Stream s = Files.list(fakeRoot)) { - s.forEach(path -> assertEquals(path.getFileName().toString(), "DirectoryIteratorException")); + s.forEach(path -> assertEquals("DirectoryIteratorException", path.getFileName().toString())); } } catch (UncheckedIOException uioe) { fail("Unexpected exception."); } fsp.setFaultyMode(true); - try { - try (DirectoryStream ds = Files.newDirectoryStream(fakeRoot)) { - Iterator itor = ds.iterator(); - while (itor.hasNext()) { - itor.next(); - } - } - fail("Shoule throw DirectoryIteratorException"); - } catch (DirectoryIteratorException die) { + try (DirectoryStream ds = Files.newDirectoryStream(fakeRoot)) { + Iterator itor = ds.iterator(); + assertThrows(DirectoryIteratorException.class, + () -> { + while (itor.hasNext()) { + itor.next(); + } + }); } try { @@ -427,7 +433,8 @@ public class StreamTest { s.forEach(path -> fail("should not get here")); } } catch (UncheckedIOException uioe) { - assertTrue(uioe.getCause() instanceof FaultyFileSystem.FaultyException); + assertInstanceOf(FaultyFileSystem.FaultyException.class, + uioe.getCause()); } catch (DirectoryIteratorException die) { fail("Should have been converted into UncheckedIOException."); } @@ -440,6 +447,7 @@ public class StreamTest { } } + @Test public void testUncheckedIOException() throws IOException { Path triggerFile = testFolder.resolve(Paths.get("dir2", "IOException")); Files.createFile(triggerFile); @@ -454,21 +462,22 @@ public class StreamTest { Path fakeRoot = fs.getRoot(); try (Stream s = Files.list(fakeRoot.resolve("dir2"))) { // only one file - s.forEach(path -> assertEquals(path.getFileName().toString(), "IOException")); + s.forEach(path -> assertEquals("IOException", path.getFileName().toString())); } try (Stream s = Files.walk(fakeRoot.resolve("empty"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); // ordered as depth-first - assertEquals(result, new String[] { "empty", "IOException", "file"}); + assertArrayEquals(new String[] { "empty", "IOException", "file"}, result); } fsp.setFaultyMode(true); try (Stream s = Files.list(fakeRoot.resolve("dir2"))) { s.forEach(path -> fail("should have caused exception")); } catch (UncheckedIOException uioe) { - assertTrue(uioe.getCause() instanceof FaultyFileSystem.FaultyException); + assertInstanceOf(FaultyFileSystem.FaultyException.class, + uioe.getCause()); } try (Stream s = Files.walk(fakeRoot.resolve("empty"))) { @@ -476,7 +485,8 @@ public class StreamTest { .toArray(String[]::new); fail("should not reach here due to IOException"); } catch (UncheckedIOException uioe) { - assertTrue(uioe.getCause() instanceof FaultyFileSystem.FaultyException); + assertInstanceOf(FaultyFileSystem.FaultyException.class, + uioe.getCause()); } try (Stream s = Files.walk( @@ -486,7 +496,7 @@ public class StreamTest { .toArray(String[]::new); fail("should not reach here due to IOException"); } catch (IOException ioe) { - assertTrue(ioe instanceof FaultyFileSystem.FaultyException); + assertInstanceOf(FaultyFileSystem.FaultyException.class, ioe); } catch (UncheckedIOException ex) { fail("Top level should be repored as is"); } @@ -500,52 +510,47 @@ public class StreamTest { } } + @Test public void testConstructException() { try (Stream s = Files.lines(testFolder.resolve("notExist"), Charset.forName("UTF-8"))) { - s.forEach(l -> fail("File is not even exist!")); + s.forEach(l -> fail("File does not even exist!")); } catch (IOException ioe) { - assertTrue(ioe instanceof NoSuchFileException); + assertInstanceOf(NoSuchFileException.class, ioe); } } + @Test public void testClosedStream() throws IOException { try (Stream s = Files.list(testFolder)) { s.close(); - Object[] actual = s.sorted().toArray(); - fail("Operate on closed stream should throw IllegalStateException"); - } catch (IllegalStateException ex) { - // expected + assertThrows(IllegalStateException.class, + () -> s.sorted().toArray()); } try (Stream s = Files.walk(testFolder)) { s.close(); - Object[] actual = s.sorted().toArray(); - fail("Operate on closed stream should throw IllegalStateException"); - } catch (IllegalStateException ex) { - // expected + assertThrows(IllegalStateException.class, + () -> s.sorted().toArray()); } try (Stream s = Files.find(testFolder, Integer.MAX_VALUE, (p, attr) -> true)) { s.close(); - Object[] actual = s.sorted().toArray(); - fail("Operate on closed stream should throw IllegalStateException"); - } catch (IllegalStateException ex) { - // expected + assertThrows(IllegalStateException.class, + () -> s.sorted().toArray()); } } + @Test + @EnabledOnOs(OS.LINUX) public void testProcFile() throws IOException { - if (System.getProperty("os.name").equals("Linux")) { - Path path = Paths.get("/proc/cpuinfo"); - if (Files.exists(path)) { - String NEW_LINE = System.getProperty("line.separator"); - String s = - Files.lines(path).collect(Collectors.joining(NEW_LINE)); - if (s.length() == 0) { - fail("Files.lines(\"" + path + "\") returns no data"); - } - } + Path path = Paths.get("/proc/cpuinfo"); + if (Files.exists(path)) { + String NEW_LINE = System.getProperty("line.separator"); + String s = + Files.lines(path).collect(Collectors.joining(NEW_LINE)); + assertNotEquals(0, s.length(), + "Files.lines(\"" + path + "\") returns no data"); } } } diff --git a/test/jdk/java/nio/file/Files/SubstDrive.java b/test/jdk/java/nio/file/Files/SubstDrive.java index 94fdeadcef3..fd21b9f007e 100644 --- a/test/jdk/java/nio/file/Files/SubstDrive.java +++ b/test/jdk/java/nio/file/Files/SubstDrive.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2020 Microsoft Corporation. All rights reserved. - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -23,32 +23,35 @@ * */ +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import java.nio.file.*; import java.util.Map; import java.util.Optional; import java.util.stream.Stream; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.*; - -import static org.testng.Assert.*; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.Test; -import org.testng.SkipException; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + /* @test * @summary Test Files' public APIs with drives created using the subst command on Windows. * @requires (os.family == "windows") * @library /test/lib .. * @build SubstDrive - * @run testng SubstDrive + * @run junit SubstDrive */ public class SubstDrive { @@ -62,26 +65,24 @@ public class SubstDrive { * deleted when the test finishes. * + Find a drive that is available for use with subst. */ - @BeforeClass - public void setup() throws IOException { + @BeforeAll + public static void setup() throws IOException { TEST_TEMP_DIRECTORY = Files.createTempDirectory("tmp"); - System.out.printf("Test directory is at %s\n", TEST_TEMP_DIRECTORY); + System.err.printf("Test directory is at %s\n", TEST_TEMP_DIRECTORY); Optional substDrive = findAvailableDrive(TEST_TEMP_DIRECTORY); - if (!substDrive.isPresent()) { - throw new SkipException( - "Could not find any available drive to use with subst, skipping the tests"); - } + Assumptions.assumeTrue(substDrive.isPresent(), + "Could not find any available drive to use with subst"); SUBST_DRIVE = substDrive.get(); - System.out.printf("Using drive %s\n with subst", SUBST_DRIVE); + System.err.printf("Using drive %s\n with subst", SUBST_DRIVE); } /** * Delete the root temporary directory together with all of its contents * when all tests finish. */ - @AfterClass - public void removeRootTempDirectory() throws IOException { + @AfterAll + public static void removeRootTempDirectory() throws IOException { TestUtil.removeAll(TEST_TEMP_DIRECTORY); } @@ -90,7 +91,7 @@ public class SubstDrive { * unmap the drive after every test so that subsequent ones can reuse * the drive. */ - @AfterMethod + @AfterEach public void deleteSubstDrive() throws IOException { Stream substitutedDrives = substFindMappedDrives(); // Only delete `SUBST_DRIVE` if it is currently being substituted @@ -114,7 +115,7 @@ public class SubstDrive { assertTrue(Files.exists(p)); Files.writeString(p, fileContents); - assertEquals(Files.readString(p), fileContents); + assertEquals(fileContents, Files.readString(p)); } /** @@ -193,7 +194,7 @@ public class SubstDrive { Map attr1 = Files.readAttributes(SUBST_DRIVE, "*"); Map attr2 = Files.readAttributes(tempDirectory, "*"); - assertEquals(attr1, attr2); + assertEquals(attr2, attr1); } /** @@ -287,19 +288,19 @@ public class SubstDrive { Path tempFile = Path.of(SUBST_DRIVE.toString(), "test.txt"); String contents = "Hello world!"; Files.writeString(tempFile, contents); - assertEquals(Files.readString(tempFile), contents); + assertEquals(contents, Files.readString(tempFile)); Path link = Path.of(SUBST_DRIVE.toString(), "link"); Files.createSymbolicLink(link, tempFile); - assertEquals(Files.readString(link), contents); - assertEquals(Files.isExecutable(link), Files.isExecutable(tempFile)); - assertEquals(Files.isReadable(link), Files.isReadable(tempFile)); - assertEquals(Files.isDirectory(link), Files.isDirectory(tempFile)); - assertEquals(Files.isHidden(link), Files.isHidden(tempFile)); - assertEquals(Files.isRegularFile(link), Files.isRegularFile(tempFile)); - assertEquals(Files.isWritable(link), Files.isWritable(tempFile)); - assertEquals(Files.getOwner(link), Files.getOwner(tempFile)); + assertEquals(contents, Files.readString(link)); + assertEquals(Files.isExecutable(tempFile), Files.isExecutable(link)); + assertEquals(Files.isReadable(tempFile), Files.isReadable(link)); + assertEquals(Files.isDirectory(tempFile), Files.isDirectory(link)); + assertEquals(Files.isHidden(tempFile), Files.isHidden(link)); + assertEquals(Files.isRegularFile(tempFile), Files.isRegularFile(link)); + assertEquals(Files.isWritable(tempFile), Files.isWritable(link)); + assertEquals(Files.getOwner(tempFile), Files.getOwner(link)); } /** @@ -319,16 +320,15 @@ public class SubstDrive { substCreate(SUBST_DRIVE, tempLink); - assertEquals( - Files.readAttributes(SUBST_DRIVE, "*"), - Files.readAttributes(tempDirectory, "*")); + assertTrue(Files.readAttributes(tempDirectory, "*") + .equals(Files.readAttributes(SUBST_DRIVE, "*"))); assertTrue(Files.isWritable(SUBST_DRIVE)); Path tempFile = Files.createTempFile(SUBST_DRIVE, "prefix", "suffix"); String contents = "Hello world!"; Files.writeString(tempFile, contents); - assertEquals(Files.readString(tempFile), contents); + assertEquals(contents, Files.readString(tempFile)); Path tempDirectory2 = Files.createTempDirectory(TEST_TEMP_DIRECTORY, "tmp"); Path copy = Path.of(tempDirectory2.toString(), "copied"); @@ -341,7 +341,7 @@ public class SubstDrive { Files.move(tempFile, cut); assertTrue(Files.notExists(tempFile)); assertTrue(Files.exists(cut)); - assertEquals(Files.readString(cut), contents); + assertEquals(contents, Files.readString(cut)); } /** @@ -361,9 +361,8 @@ public class SubstDrive { substCreate(SUBST_DRIVE, tempLink); - assertEquals( - Files.readAttributes(SUBST_DRIVE, "*"), - Files.readAttributes(tempDirectory, "*")); + assertTrue(Files.readAttributes(tempDirectory, "*") + .equals(Files.readAttributes(SUBST_DRIVE, "*"))); assertTrue(Files.isWritable(SUBST_DRIVE)); } @@ -372,7 +371,7 @@ public class SubstDrive { * Run a command and optionally prints stdout contents to * `customOutputStream`. */ - private void runCmd(ProcessBuilder pb, PrintStream customOutputStream) { + private static void runCmd(ProcessBuilder pb, PrintStream customOutputStream) { try { PrintStream ps = customOutputStream != null ? customOutputStream : @@ -383,8 +382,8 @@ public class SubstDrive { int exitCode = outputAnalyzer.getExitValue(); assertEquals( - exitCode /* actual value */, 0 /* expected value */, + exitCode /* actual value */, String.format( "Command `%s` failed with exit code %d", pb.command(), @@ -402,7 +401,7 @@ public class SubstDrive { * For reference, see: * https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/subst */ - private void substCreate(Path drive, Path path) { + private static void substCreate(Path drive, Path path) { runCmd( new ProcessBuilder( "cmd", "/c", "subst", drive.toString(), path.toString()), @@ -414,7 +413,7 @@ public class SubstDrive { * For reference, see: * https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/subst */ - private void substDelete(Path drive) throws IOException { + private static void substDelete(Path drive) throws IOException { runCmd( new ProcessBuilder( "cmd", "/c", "subst", drive.toString(), "/D"), @@ -454,7 +453,7 @@ public class SubstDrive { * subst can fail if the drive to be mapped already exists. The method returns * a drive that is available. */ - private Optional findAvailableDrive(Path tempDirectory) { + private static Optional findAvailableDrive(Path tempDirectory) { for (char letter = 'Z'; letter >= 'A'; letter--) { try { Path p = Path.of(letter + ":"); diff --git a/test/jdk/java/nio/file/Files/walkFileTree/FindTest.java b/test/jdk/java/nio/file/Files/walkFileTree/FindTest.java index 9fb16a7b14c..beacee44bbb 100644 --- a/test/jdk/java/nio/file/Files/walkFileTree/FindTest.java +++ b/test/jdk/java/nio/file/Files/walkFileTree/FindTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, 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 @@ -34,15 +34,15 @@ * jdk.test.lib.Platform * jdk.test.lib.process.* * CreateFileTree - * @run testng/othervm -Djava.io.tmpdir=. FindTest + * @run junit/othervm -Djava.io.tmpdir=. FindTest */ import java.io.IOException; +import java.nio.file.Files; import java.nio.file.FileSystemLoopException; import java.nio.file.FileVisitOption; -import java.nio.file.FileVisitResult; import java.nio.file.FileVisitor; -import java.nio.file.Files; +import java.nio.file.FileVisitResult; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; @@ -56,11 +56,11 @@ import java.util.stream.Collectors; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class FindTest { @@ -69,7 +69,7 @@ public class FindTest { private static Path top; private static String TOP; - @BeforeClass + @BeforeAll public static void createFileTree() throws Exception { top = CreateFileTree.create(); TOP = top.toAbsolutePath().toString(); @@ -107,7 +107,7 @@ public class FindTest { throws IOException { List expectedList = Arrays.asList(expected.getStdout() .split(System.lineSeparator())); - assertEquals(actual.size(), expectedList.size()); + assertEquals(expectedList.size(), actual.size()); assertTrue(actual.removeAll(expectedList)); } diff --git a/test/jdk/java/nio/file/spi/TestDelegation.java b/test/jdk/java/nio/file/spi/TestDelegation.java index 0b9bc9de7f7..f09b636c156 100644 --- a/test/jdk/java/nio/file/spi/TestDelegation.java +++ b/test/jdk/java/nio/file/spi/TestDelegation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -21,11 +21,6 @@ * questions. */ -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import java.io.IOException; import java.net.URI; import java.nio.file.*; @@ -34,26 +29,34 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Stream; -import static org.testng.AssertJUnit.assertEquals; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @test * @summary Verifies that a FileSystemProvider's implementation of the exists * and readAttributesIfExists methods are invoked * @compile testfsp/testfsp/TestProvider.java - * @run testng TestDelegation + * @run junit TestDelegation */ public class TestDelegation { // Non-existent Path to be used by the test - private Path nonExistentFile; + private static Path nonExistentFile; // Path to Temp directory used by the test - private Path tempDirectory; + private static Path tempDirectory; // Valid file Path used by the test - private Path fileThatExists; + private static Path fileThatExists; // The FileSystemProvider used by the test - private MyProvider myProvider; + private static MyProvider myProvider; /** * Create the FileSystemProvider, the FileSystem and @@ -61,8 +64,8 @@ public class TestDelegation { * * @throws IOException if an error occurs */ - @BeforeClass - public void setup() throws IOException { + @BeforeAll + public static void setup() throws IOException { myProvider = new MyProvider(); FileSystem fs = myProvider.getFileSystem(URI.create("/")); // Path to Current Working Directory @@ -73,7 +76,7 @@ public class TestDelegation { } /** - * DataProvider that is used to test Files::exists. The DataProvider's + * MethodSource that is used to test Files::exists. The Arguments' * elements are: *
    *
  • Path to validate
  • @@ -81,17 +84,14 @@ public class TestDelegation { *
* @return The test parameter data */ - @DataProvider - private Object[][] testExists() { - return new Object[][]{ - {tempDirectory, true}, - {fileThatExists, true}, - {nonExistentFile, false} - }; + private static Stream testExists() { + return Stream.of(Arguments.of(tempDirectory, true), + Arguments.of(fileThatExists, true), + Arguments.of(nonExistentFile, false)); } /** - * DataProvider that is used to test Files::isDirectory. The DataProvider's + * MethodSource that is used to test Files::isDirectory. The Arguments' * elements are: *
    *
  • Path to validate
  • @@ -99,16 +99,13 @@ public class TestDelegation { *
* @return The test parameter data */ - @DataProvider - private Object[][] testIsDirectory() { - return new Object[][]{ - {tempDirectory, true}, - {fileThatExists, false}, - {nonExistentFile, false} - }; + private static Stream testIsDirectory() { + return Stream.of(Arguments.of(tempDirectory, true), + Arguments.of(fileThatExists, false), + Arguments.of(nonExistentFile, false)); } /** - * DataProvider that is used to test Files::isRegularFile. The DataProvider's + * MethodSource that is used to test Files::isRegularFile. The MethodSource's * elements are: *
    *
  • Path to validate
  • @@ -116,19 +113,16 @@ public class TestDelegation { *
* @return The test parameter data */ - @DataProvider - private Object[][] testIsRegularFile() { - return new Object[][]{ - {tempDirectory, false}, - {fileThatExists, true}, - {nonExistentFile, false} - }; + private static Stream testIsRegularFile() { + return Stream.of(Arguments.of(tempDirectory, false), + Arguments.of(fileThatExists, true), + Arguments.of(nonExistentFile, false)); } /** * Clear our Map prior to each test run */ - @BeforeMethod + @BeforeEach public void resetParams() { myProvider.resetCalls(); } @@ -140,9 +134,10 @@ public class TestDelegation { * @param p the path to the file to test * @param exists does the path exist */ - @Test(dataProvider = "testExists") + @ParameterizedTest + @MethodSource("testExists") public void testExists(Path p, boolean exists) { - assertEquals(Files.exists(p), exists); + assertEquals(exists, Files.exists(p)); // We should only have called exists once assertEquals(1, myProvider.findCall("exists").size()); assertEquals(0, myProvider.findCall("readAttributesIfExists").size()); @@ -155,9 +150,10 @@ public class TestDelegation { * @param p the path to the file to test * @param isDir is the path a directory */ - @Test(dataProvider = "testIsDirectory") + @ParameterizedTest + @MethodSource("testIsDirectory") public void testIsDirectory(Path p, boolean isDir) { - assertEquals(Files.isDirectory(p), isDir); + assertEquals(isDir, Files.isDirectory(p)); // We should only have called readAttributesIfExists once assertEquals(0, myProvider.findCall("exists").size()); assertEquals(1, myProvider.findCall("readAttributesIfExists").size()); @@ -170,9 +166,10 @@ public class TestDelegation { * @param p the path to the file to test * @param isFile is the path a regular file */ - @Test(dataProvider = "testIsRegularFile") + @ParameterizedTest + @MethodSource("testIsRegularFile") public void testIsRegularFile(Path p, boolean isFile) { - assertEquals(Files.isRegularFile(p), isFile); + assertEquals(isFile, Files.isRegularFile(p)); // We should only have called readAttributesIfExists once assertEquals(0, myProvider.findCall("exists").size()); assertEquals(1, myProvider.findCall("readAttributesIfExists").size()); diff --git a/test/jdk/java/util/ResourceBundle/modules/ModuleTestUtil.java b/test/jdk/java/util/ResourceBundle/modules/ModuleTestUtil.java index 24c6e181ccc..ec4c2355d18 100644 --- a/test/jdk/java/util/ResourceBundle/modules/ModuleTestUtil.java +++ b/test/jdk/java/util/ResourceBundle/modules/ModuleTestUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -28,8 +28,6 @@ import java.nio.file.Paths; import java.util.List; import java.util.stream.Stream; -import jdk.test.lib.JDKToolLauncher; -import jdk.test.lib.Utils; import jdk.test.lib.compiler.CompilerUtils; import jdk.test.lib.process.ProcessTools; @@ -185,30 +183,4 @@ public class ModuleTestUtil { } } } - - /** - * Run the module test with "useOldISOCodes=true". - * - * @param mp module path - * @param mn module name - * @param localeList locale list - */ - public static void runModuleWithLegacyCode(String mp, String mn, List localeList) - throws Throwable { - List args = List.of( - "-ea", "-esa", - "-Djava.locale.useOldISOCodes=true", - "-p", mp, - "-m", mn); - // Build process (with VM flags) - ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( - Stream.concat(args.stream(), localeList.stream()).toList()); - // Evaluate process status - int exitCode = ProcessTools.executeCommand(pb).getExitValue(); - - if (exitCode != 0) { - throw new RuntimeException("Execution of the test failed. " - + "Unexpected exit code: " + exitCode); - } - } -} \ No newline at end of file +} diff --git a/test/jdk/java/util/ResourceBundle/modules/basic/BasicTest.java b/test/jdk/java/util/ResourceBundle/modules/basic/BasicTest.java index c17c4622ecb..f3432a733b4 100644 --- a/test/jdk/java/util/ResourceBundle/modules/basic/BasicTest.java +++ b/test/jdk/java/util/ResourceBundle/modules/basic/BasicTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -43,7 +43,7 @@ * jdk.test.lib.compiler.CompilerUtils * jdk.test.lib.process.ProcessTools * ModuleTestUtil - * @run junit BasicTest + * @run junit/timeout=200 BasicTest */ import java.nio.file.Path; @@ -132,7 +132,6 @@ public class BasicTest { moduleList.forEach(mn -> ModuleTestUtil.prepareModule(srcPath, modPath, mn, resFormat)); ModuleTestUtil.runModule(modPath.toString(), MAIN, localeList); - ModuleTestUtil.runModuleWithLegacyCode(modPath.toString(), MAIN, localeList); } @Test @@ -206,4 +205,4 @@ public class BasicTest { assertEquals(exitCode, 0, "Create extra_modlocal.jar failed. " + "Unexpected exit code: " + exitCode); } -} \ No newline at end of file +} diff --git a/test/jdk/java/util/jar/Manifest/IncludeInExceptionsTest.java b/test/jdk/java/util/jar/Manifest/IncludeInExceptionsTest.java index bb3cd6e23ef..52d3783b4cf 100644 --- a/test/jdk/java/util/jar/Manifest/IncludeInExceptionsTest.java +++ b/test/jdk/java/util/jar/Manifest/IncludeInExceptionsTest.java @@ -21,9 +21,6 @@ * questions. */ -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; @@ -34,17 +31,23 @@ import java.util.jar.JarFile; import java.util.jar.JarOutputStream; import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; +import static sun.security.util.SecurityProperties.INCLUDE_JAR_NAME_IN_EXCEPTIONS; /* * @test * @bug 8216362 - * @run junit/othervm -Djdk.includeInExceptions=jar IncludeInExceptionsTest - * @run junit/othervm IncludeInExceptionsTest * @summary Verify that the property jdk.includeInExceptions works as expected - * when an error occurs while reading an invalid Manifest file. + * when an error occurs while reading an invalid Manifest file. + * @modules java.base/sun.security.util + * @run junit/othervm -Djdk.includeInExceptions=jar IncludeInExceptionsTest + * @run junit/othervm -Djdk.includeInExceptions= IncludeInExceptionsTest + * @run junit/othervm IncludeInExceptionsTest */ /* @@ -53,8 +56,6 @@ import static org.junit.jupiter.api.Assertions.fail; */ public class IncludeInExceptionsTest { - private static final boolean includeInExceptions = System.getProperty("jdk.includeInExceptions") != null; - static final String FILENAME = "Unique-Filename-Expected-In_Msg.jar"; static final byte[] INVALID_MANIFEST = ( @@ -79,9 +80,9 @@ public class IncludeInExceptionsTest { void testInvalidManifest(Callable attempt) { var ioException = assertThrows(IOException.class, attempt::call); boolean foundFileName = ioException.getMessage().contains(FILENAME); - if (includeInExceptions && !foundFileName) { + if (INCLUDE_JAR_NAME_IN_EXCEPTIONS && !foundFileName) { fail("JAR file name expected but not found in error message"); - } else if (foundFileName && !includeInExceptions) { + } else if (foundFileName && !INCLUDE_JAR_NAME_IN_EXCEPTIONS) { fail("JAR file name found but should not be in error message"); } } @@ -96,4 +97,3 @@ public class IncludeInExceptionsTest { ); } } - diff --git a/test/jdk/java/util/logging/ParentLoggersTest.java b/test/jdk/java/util/logging/ParentLoggersTest.java index 8aae7e95d74..55a93249ca8 100644 --- a/test/jdk/java/util/logging/ParentLoggersTest.java +++ b/test/jdk/java/util/logging/ParentLoggersTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6498300 8377457 + * @bug 6498300 8377457 8379548 * * @summary regression: parent loggers are not properly registered * @author ss45998 diff --git a/test/jdk/java/util/prefs/PrefsSpiTest.java b/test/jdk/java/util/prefs/PrefsSpiTest.java index 0369c60cae2..08fff81a552 100644 --- a/test/jdk/java/util/prefs/PrefsSpiTest.java +++ b/test/jdk/java/util/prefs/PrefsSpiTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -28,7 +28,7 @@ * @library /test/lib * @build jdk.test.lib.util.JarUtils jdk.test.lib.process.* * PrefsSpi StubPreferencesFactory StubPreferences - * @run testng PrefsSpiTest + * @run junit PrefsSpiTest */ import java.io.File; @@ -36,16 +36,13 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; import jdk.test.lib.JDKToolFinder; import jdk.test.lib.Utils; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.util.JarUtils; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static java.nio.file.StandardOpenOption.CREATE; import static java.util.Arrays.asList; @@ -53,14 +50,19 @@ import static java.util.Arrays.asList; import static jdk.test.lib.Utils.TEST_CLASSES; import static jdk.test.lib.Utils.TEST_CLASS_PATH; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + public class PrefsSpiTest { private static final Path SPIJAR = Path.of("extDir", "PrefsSpi.jar"); private static final String SPIJAR_CP = TEST_CLASS_PATH + File.pathSeparator + SPIJAR.toString(); - @BeforeClass - public void initialize() throws Exception { + @BeforeAll + public static void initialize() throws Exception { Path xdir = Path.of("jarDir"); Path config = xdir.resolve("META-INF/services/java.util.prefs.PreferencesFactory"); @@ -77,19 +79,18 @@ public class PrefsSpiTest { JarUtils.createJarFile(SPIJAR, xdir); } - @DataProvider - public Object[][] testCases() { - return new Object[][]{ - // CLI options, runtime arguments - {List.of("-cp", SPIJAR_CP, - "-Djava.util.prefs.PreferencesFactory=StubPreferencesFactory"), - "StubPreferences"}, - {List.of("-cp", TEST_CLASS_PATH), "java.util.prefs.*"}, - {List.of("-cp", SPIJAR_CP), "StubPreferences"} - }; + public static Stream testCases() { + return Stream.of + (// CLI options, runtime arguments + Arguments.of(List.of("-cp", SPIJAR_CP, + "-Djava.util.prefs.PreferencesFactory=StubPreferencesFactory"), + "StubPreferences"), + Arguments.of(List.of("-cp", TEST_CLASS_PATH), "java.util.prefs.*"), + Arguments.of(List.of("-cp", SPIJAR_CP), "StubPreferences")); } - @Test(dataProvider = "testCases") + @ParameterizedTest + @MethodSource("testCases") public void testProvider(List opts, String pattern) throws Throwable { List args = new ArrayList<>(); args.add(JDKToolFinder.getJDKTool("java")); diff --git a/test/jdk/java/util/zip/InflaterOutputStreamTest.java b/test/jdk/java/util/zip/InflaterOutputStreamTest.java new file mode 100644 index 00000000000..3f588d95c0c --- /dev/null +++ b/test/jdk/java/util/zip/InflaterOutputStreamTest.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2026, 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.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.Deflater; +import java.util.zip.Inflater; +import java.util.zip.InflaterOutputStream; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; + +/* + * @test + * @bug 8369181 + * @summary Verify the behaviour of basic operations on InflaterOutputStream + * @run junit ${test.main.class} + */ +class InflaterOutputStreamTest { + + private static final byte[] INPUT_CONTENT = "hello world".getBytes(US_ASCII); + private static byte[] COMPRESSED_CONTENT; + + @BeforeAll + static void beforeAll() throws Exception { + // created the compressed content + final ByteArrayOutputStream compressedBaos = new ByteArrayOutputStream(); + try (Deflater compressor = new Deflater()) { + compressor.setInput(INPUT_CONTENT); + compressor.finish(); + while (!compressor.finished()) { + final byte[] tmpBuffer = new byte[100]; + final int numCompressed = compressor.deflate(tmpBuffer); + compressedBaos.write(tmpBuffer, 0, numCompressed); + } + } + COMPRESSED_CONTENT = compressedBaos.toByteArray(); + } + + static List inflaterOutputStreams() { + final List args = new ArrayList<>(); + args.add(Arguments.of(new InflaterOutputStream(new ByteArrayOutputStream()))); + args.add(Arguments.of(new InflaterOutputStream(new ByteArrayOutputStream(), + new Inflater()))); + args.add(Arguments.of(new InflaterOutputStream(new ByteArrayOutputStream(), + new Inflater(), 1024))); + return args; + } + + /* + * Verify that write() methods throw an IOException when the InflaterOutputStream + * is already closed. + */ + @ParameterizedTest + @MethodSource("inflaterOutputStreams") + void testWriteAfterClose(final InflaterOutputStream ios) throws Exception { + ios.close(); + IOException ioe = assertThrows(IOException.class, () -> ios.write(COMPRESSED_CONTENT)); + // verify it failed for right reason + assertStreamClosedIOE(ioe); + } + + /* + * Verify that flush() throws an IOException when the InflaterOutputStream + * is already closed + */ + @ParameterizedTest + @MethodSource("inflaterOutputStreams") + void testFlushAfterClose(final InflaterOutputStream ios) throws Exception { + ios.close(); + final IOException ioe = assertThrows(IOException.class, ios::flush); + // verify it failed for right reason + assertStreamClosedIOE(ioe); + } + + /* + * Verify that finish() throws an IOException when the InflaterOutputStream + * is already closed. + */ + @ParameterizedTest + @MethodSource("inflaterOutputStreams") + void testFinishAfterClose(final InflaterOutputStream ios) throws Exception { + ios.close(); + final IOException ioe = assertThrows(IOException.class, ios::finish); + // verify it failed for right reason + assertStreamClosedIOE(ioe); + } + + /* + * Verify that after finish() is called on an InflaterOutputStream that was constructed + * without specifying a Inflater, any subsequent writes will throw an IOException. + */ + @Test + void testWriteAfterFinish() throws IOException { + // InflaterOutputStream that constructs an internal default Inflater + final InflaterOutputStream ios = new InflaterOutputStream(new ByteArrayOutputStream()); + ios.finish(); + final IOException ioe = assertThrows(IOException.class, () -> ios.write(COMPRESSED_CONTENT)); + final String msg = ioe.getMessage(); + // verify that it's the right IOException + if (msg == null || !msg.contains("Inflater closed")) { + // propagate the original exception + fail("unexpected exception message in IOException", ioe); + } + + // repeat with a InflaterOutputStream that is passed a Inflater + try (Inflater intf = new Inflater(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + InflaterOutputStream stream = new InflaterOutputStream(baos, intf)) { + + stream.finish(); + // not expected to throw any exception + stream.write(COMPRESSED_CONTENT); + stream.flush(); + // verify the decompressed content is as expected + final byte[] decompressed = baos.toByteArray(); + assertArrayEquals(INPUT_CONTENT, decompressed, "unexpected decompressed content"); + } + } + + private void assertStreamClosedIOE(final IOException ioe) { + final String msg = ioe.getMessage(); + if (msg == null || !msg.contains("Stream closed")) { + // propagate the original exception + fail("unexpected exception message in IOException", ioe); + } + } +} diff --git a/test/jdk/javax/swing/JComponent/8043610/bug8043610.java b/test/jdk/javax/swing/JComponent/8043610/bug8043610.java deleted file mode 100644 index 7eb371d1a0e..00000000000 --- a/test/jdk/javax/swing/JComponent/8043610/bug8043610.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2014, 2017, 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 - * @key headful - * @bug 8043610 - * @summary Tests that JComponent invalidate, revalidate and repaint methods could - * be called from any thread - * @author Petr Pchelko - * @modules java.desktop/sun.awt - */ - -import sun.awt.SunToolkit; - -import javax.swing.*; -import java.awt.*; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicReference; - -public class bug8043610 { - private static volatile JFrame frame; - private static volatile JComponent component; - - public static void main(String[] args) throws Exception { - ThreadGroup stubTG = new ThreadGroup(getRootThreadGroup(), "Stub Thread Group"); - ThreadGroup swingTG = new ThreadGroup(getRootThreadGroup(), "SwingTG"); - try { - Thread stubThread = new Thread(stubTG, SunToolkit::createNewAppContext); - stubThread.start(); - stubThread.join(); - - CountDownLatch startSwingLatch = new CountDownLatch(1); - new Thread(swingTG, () -> { - SunToolkit.createNewAppContext(); - SwingUtilities.invokeLater(() -> { - frame = new JFrame(); - component = new JLabel("Test Text"); - frame.add(component); - frame.setBounds(100, 100, 100, 100); - frame.setVisible(true); - startSwingLatch.countDown(); - }); - }).start(); - startSwingLatch.await(); - - AtomicReference caughtException = new AtomicReference<>(); - Thread checkThread = new Thread(getRootThreadGroup(), () -> { - try { - component.invalidate(); - component.revalidate(); - component.repaint(new Rectangle(0, 0, 0, 0)); - } catch (Exception e) { - caughtException.set(e); - } - }); - checkThread.start(); - checkThread.join(); - - if (caughtException.get() != null) { - throw new RuntimeException("Failed. Caught exception!", caughtException.get()); - } - } finally { - new Thread(swingTG, () -> SwingUtilities.invokeLater(() -> { - if (frame != null) { - frame.dispose(); - } - })).start(); - } - } - - private static ThreadGroup getRootThreadGroup() { - ThreadGroup currentTG = Thread.currentThread().getThreadGroup(); - ThreadGroup parentTG = currentTG.getParent(); - while (parentTG != null) { - currentTG = parentTG; - parentTG = currentTG.getParent(); - } - return currentTG; - } -} diff --git a/test/jdk/javax/swing/JInternalFrame/TestNonMaximizedNormalBounds.java b/test/jdk/javax/swing/JInternalFrame/TestNonMaximizedNormalBounds.java new file mode 100644 index 00000000000..67faeeb5bb1 --- /dev/null +++ b/test/jdk/javax/swing/JInternalFrame/TestNonMaximizedNormalBounds.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2026, 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 + * @key headful + * @bug 8267961 + * @summary Verify JInternalFrame.getNormalBounds() + * returns getBounds() value in non-maximized state + * @run main TestNonMaximizedNormalBounds + */ + +import java.awt.Rectangle; +import javax.swing.JDesktopPane; +import javax.swing.JInternalFrame; +import javax.swing.SwingUtilities; +import java.beans.PropertyVetoException; + +public class TestNonMaximizedNormalBounds { + + private static volatile Rectangle bounds; + private static volatile Rectangle normalBounds; + private static JInternalFrame jif; + + public static void main(String[] args) throws Exception { + SwingUtilities.invokeAndWait(() -> { + Rectangle bounds = new Rectangle(96, 97, 98, 99); + Rectangle nbounds = new Rectangle(196, 197, 198, 199); + JDesktopPane p = new JDesktopPane(); + jif = new JInternalFrame(); + p.add(jif); + jif.setBounds(bounds); + jif.setNormalBounds(nbounds); + }); + Thread.sleep(100); + SwingUtilities.invokeAndWait(() -> { + try { + jif.setMaximum(false); + } catch (PropertyVetoException e) { + throw new RuntimeException(e); + } + }); + Thread.sleep(100); + SwingUtilities.invokeAndWait(() -> { + normalBounds = jif.getNormalBounds(); + bounds = jif.getBounds(); + }); + if (!normalBounds.equals(bounds)) { + System.out.println("normalBounds " + normalBounds + " getBounds " + bounds); + throw new RuntimeException("normalBounds not equal to getBounds in non-maximized state"); + } + } +} diff --git a/test/jdk/javax/swing/JTable/8236907/LastVisibleRow.java b/test/jdk/javax/swing/JTable/8236907/LastVisibleRow.java index 8b60bf8beec..ebd563624cd 100644 --- a/test/jdk/javax/swing/JTable/8236907/LastVisibleRow.java +++ b/test/jdk/javax/swing/JTable/8236907/LastVisibleRow.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -20,6 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + /* * @test * @key headful @@ -59,11 +60,9 @@ public class LastVisibleRow { Point clkPoint; try { testRobot = new Robot(); - SwingUtilities.invokeAndWait(new Runnable() { - public void run() { - createAndShowGUI(); - } - }); + testRobot.setAutoWaitForIdle(true); + testRobot.setAutoDelay(30); + SwingUtilities.invokeAndWait(LastVisibleRow::createAndShowGUI); testRobot.delay(1000); testRobot.waitForIdle(); BufferedImage bufferedImageBefore = testRobot.createScreenCapture(getCaptureRect()); @@ -73,7 +72,7 @@ public class LastVisibleRow { mouseEvents(clkPoint); testRobot.waitForIdle(); clearSelect(); - testRobot.delay(1000); + resetMousePos(); testRobot.waitForIdle(); BufferedImage bufferedImageAfter = testRobot.createScreenCapture(getCaptureRect()); @@ -137,43 +136,37 @@ public class LastVisibleRow { testRobot.delay(50); } + private static void resetMousePos() { + testRobot.mouseMove(50, 50); + testRobot.delay(50); + } + // getMousePosition Actions for last row click private static Point getMousePosition() throws Exception { final Point[] clickPoint = new Point[1]; - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - clickPoint[0] = getCellClickPoint(2, 0); - } - }); + SwingUtilities.invokeAndWait((Runnable) () -> clickPoint[0] = getCellClickPoint(2, 0)); return clickPoint[0]; } // Clears the selected table row private static void clearSelect() throws Exception { - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - table.getSelectionModel().clearSelection(); - table.setFocusable(false); - } + SwingUtilities.invokeAndWait((Runnable) () -> { + table.getSelectionModel().clearSelection(); + table.setFocusable(false); }); } // getCaptureRect Method - To Compute the Rectangle Area of last row private static Rectangle getCaptureRect() throws InterruptedException, InvocationTargetException { final Rectangle[] captureRect = new Rectangle[1]; - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - Rectangle cellRect = table.getCellRect(2, 0, true); - Point point = new Point(cellRect.x, cellRect.y); - SwingUtilities.convertPointToScreen(point, table); + SwingUtilities.invokeAndWait((Runnable) () -> { + Rectangle cellRect = table.getCellRect(2, 0, true); + Point point = new Point(cellRect.x, cellRect.y); + SwingUtilities.convertPointToScreen(point, table); - captureRect[0] = new Rectangle(point.x+5, point.y+2, - table.getColumnCount() * cellRect.width - 10, - cellRect.height-2); - } + captureRect[0] = new Rectangle(point.x + 5, point.y + 2, + table.getColumnCount() * cellRect.width - 10, + cellRect.height - 2); }); return captureRect[0]; } diff --git a/test/jdk/jdk/classfile/VerifierSelfTest.java b/test/jdk/jdk/classfile/VerifierSelfTest.java index 7d1dae6f519..12458b1586e 100644 --- a/test/jdk/jdk/classfile/VerifierSelfTest.java +++ b/test/jdk/jdk/classfile/VerifierSelfTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -37,12 +37,14 @@ import static java.lang.constant.ConstantDescs.*; import java.lang.constant.MethodTypeDesc; import java.lang.invoke.MethodHandleInfo; +import java.lang.invoke.MethodHandles; import java.net.URI; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -59,6 +61,9 @@ import jdk.internal.classfile.impl.BufWriterImpl; import jdk.internal.classfile.impl.DirectClassBuilder; import jdk.internal.classfile.impl.UnboundAttribute; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import static org.junit.jupiter.api.Assertions.*; @@ -418,6 +423,71 @@ class VerifierSelfTest { return lst; } + enum ComparisonInstruction { + IF_ACMPEQ(Opcode.IF_ACMPEQ, 2), + IF_ACMPNE(Opcode.IF_ACMPNE, 2), + IFNONNULL(Opcode.IFNONNULL, 1), + IFNULL(Opcode.IFNULL, 1); + final Opcode opcode; + final int argCount; + ComparisonInstruction(Opcode opcode, int argCount) { + this.opcode = opcode; + this.argCount = argCount; + } + } + + enum UninitializeKind { + UNINITIALIZED, UNINITIALIZED_THIS + } + + @ParameterizedTest + @MethodSource("uninitializedInBytecodeClasses") + public void testUninitializedInComparisons(ComparisonInstruction inst, UninitializeKind kind) throws Throwable { + var bytes = ClassFile.of(ClassFile.StackMapsOption.DROP_STACK_MAPS).build(ClassDesc.of("Test"), clb -> clb + .withMethodBody(INIT_NAME, MTD_void, 0, cob -> { + StackMapFrameInfo.VerificationTypeInfo uninitializeInfo; + if (kind == UninitializeKind.UNINITIALIZED) { + uninitializeInfo = StackMapFrameInfo.UninitializedVerificationTypeInfo.of(cob.newBoundLabel()); + cob.new_(CD_Object); + } else { + uninitializeInfo = StackMapFrameInfo.SimpleVerificationTypeInfo.UNINITIALIZED_THIS; + cob.aload(0); + } + + // Stack: uninitializeInfo + for (int i = 0; i < inst.argCount; i++) { + cob.dup(); + } + var dest = cob.newLabel(); + cob.branch(inst.opcode, dest) + .nop() + .labelBinding(dest) + .with(StackMapTableAttribute.of(List.of(StackMapFrameInfo.of(dest, + List.of(StackMapFrameInfo.SimpleVerificationTypeInfo.UNINITIALIZED_THIS), + List.of(uninitializeInfo))))) + .invokespecial(CD_Object, INIT_NAME, MTD_void); + if (kind == UninitializeKind.UNINITIALIZED) { + // still need to call super constructor + cob.aload(0) + .invokespecial(CD_Object, INIT_NAME, MTD_void); + } + cob.return_(); + })); + var errors = ClassFile.of().verify(bytes); + assertNotEquals(List.of(), errors, () -> errors + " : " + ClassFile.of().parse(bytes).toDebugString()); + var lookup = MethodHandles.lookup(); + assertThrows(VerifyError.class, () -> lookup.defineHiddenClass(bytes, true)); // force JVM verification + } + + public static Stream uninitializedInBytecodeClasses() { + return Arrays.stream(ComparisonInstruction.values()) + .mapMulti((inst, sink) -> { + for (var kind : UninitializeKind.values()) { + sink.accept(Arguments.of(inst, kind)); + } + }); + } + @Test // JDK-8350029 void testInvokeSpecialInterfacePatch() { var runClass = ClassDesc.of("Run"); diff --git a/test/jdk/jdk/jfr/event/compiler/TestCompilerInlining.java b/test/jdk/jdk/jfr/event/compiler/TestCompilerInlining.java index 9694c054f6a..20e611fc671 100644 --- a/test/jdk/jdk/jfr/event/compiler/TestCompilerInlining.java +++ b/test/jdk/jdk/jfr/event/compiler/TestCompilerInlining.java @@ -131,10 +131,10 @@ public class TestCompilerInlining { if (WHITE_BOX.getBooleanVMFlag("TieredCompilation")) { return IntStream.rangeClosed(LEVEL_SIMPLE, WHITE_BOX.getIntxVMFlag("TieredStopAtLevel").intValue()).toArray(); } - if (Platform.isServer() && !Platform.isEmulatedClient()) { + if (Platform.isServer()) { return new int[] { LEVEL_FULL_OPTIMIZATION }; } - if (Platform.isClient() || Platform.isEmulatedClient()) { + if (Platform.isClient()) { return new int[] { LEVEL_SIMPLE }; } throw new Error("TESTBUG: unknown VM"); diff --git a/test/jdk/jdk/jfr/event/compiler/TestCompilerPhase.java b/test/jdk/jdk/jfr/event/compiler/TestCompilerPhase.java index 33884d448bf..0433e157f3d 100644 --- a/test/jdk/jdk/jfr/event/compiler/TestCompilerPhase.java +++ b/test/jdk/jdk/jfr/event/compiler/TestCompilerPhase.java @@ -42,7 +42,6 @@ import jdk.test.whitebox.WhiteBox; * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -Xbootclasspath/a:. - * -XX:-NeverActAsServerClassMachine * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:CompileOnly=jdk.jfr.event.compiler.TestCompilerPhase::dummyMethod * -XX:+SegmentedCodeCache -Xbootclasspath/a:. diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1OldAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1OldAllocationPendingStackTrace.java index 1346c36c04f..64389a0e921 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1OldAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1OldAllocationPendingStackTrace.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -29,7 +29,7 @@ package jdk.jfr.event.gc.stacktrace; * * @requires vm.gc == "null" | vm.gc == "G1" * @library /test/lib /test/jdk - * @run main/othervm -XX:MaxNewSize=10M -Xmx128M -XX:+UseG1GC -Xlog:gc* + * @run main/othervm -XX:MaxNewSize=10M -Xmx64M -XX:+UseG1GC -Xlog:gc* * -XX:FlightRecorderOptions:stackdepth=256 * jdk.jfr.event.gc.stacktrace.TestG1OldAllocationPendingStackTrace */ diff --git a/test/jdk/jdk/jfr/event/oldobject/OldObjects.java b/test/jdk/jdk/jfr/event/oldobject/OldObjects.java index ba90bb10a9e..bb0ca27836e 100644 --- a/test/jdk/jdk/jfr/event/oldobject/OldObjects.java +++ b/test/jdk/jdk/jfr/event/oldobject/OldObjects.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -276,4 +276,16 @@ final public class OldObjects { throw new RuntimeException("Reference chain max length not respected. Found a chain of length " + length); } } + + public static int countChains(List events) throws IOException { + int found = 0; + for (RecordedEvent e : events) { + RecordedObject ro = e.getValue("object"); + if (ro.getValue("referrer") != null) { + found++; + } + } + System.out.println("Found chains: " + found); + return found; + } } diff --git a/test/jdk/jdk/jfr/event/oldobject/TestDFSWithSmallStack.java b/test/jdk/jdk/jfr/event/oldobject/TestDFSWithSmallStack.java new file mode 100644 index 00000000000..d25a6cd5f67 --- /dev/null +++ b/test/jdk/jdk/jfr/event/oldobject/TestDFSWithSmallStack.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corp. + * 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 jdk.jfr.event.oldobject; + +import java.util.LinkedList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test id=dfsonly + * @summary Tests that DFS works with a small stack + * @library /test/lib /test/jdk + * @requires vm.hasJFR + * @modules jdk.jfr/jdk.jfr.internal.test + * @run main/othervm -Xmx2g -XX:VMThreadStackSize=512 jdk.jfr.event.oldobject.TestDFSWithSmallStack dfsonly + */ + +/** + * @test id=bfsdfs + * @summary Tests that DFS works with a small stack + * @library /test/lib /test/jdk + * @requires vm.hasJFR + * @modules jdk.jfr/jdk.jfr.internal.test + * @run main/othervm -Xmx2g -XX:VMThreadStackSize=512 jdk.jfr.event.oldobject.TestDFSWithSmallStack bfsdfs + */ +public class TestDFSWithSmallStack { + + // Tests depth first search with a small stack. + + // An non-zero exit code, together with a missing hs-err file or possibly a missing jfr file, + // indicates a native stack overflow happened and is a fail condition for this test. + + // We build up an array of linked lists, each containing enough entries for DFS search to + // max out max_dfs_depth (but not greatly surpass it). + + private static final int TOTAL_OBJECTS = 10_000_000; + private static final int OBJECTS_PER_LIST = 5_000; + public static LinkedList[] leak; + + public static void main(String... args) throws Exception { + + switch (args[0]) { + case "dfsonly" -> WhiteBox.setSkipBFS(true); + case "bfsdfs" -> {} /* ignored */ + default -> throw new RuntimeException("Invalid argument"); + } + + WhiteBox.setWriteAllObjectSamples(true); + int count = 10; + + while (count > 0) { + try (Recording r = new Recording()) { + r.enable(EventNames.OldObjectSample).with("cutoff", "infinity"); + r.start(); + leak = new LinkedList[TOTAL_OBJECTS / OBJECTS_PER_LIST]; + for (int i = 0; i < leak.length; i++) { + leak[i] = new LinkedList(); + for (int j = 0; j < OBJECTS_PER_LIST; j++) { + leak[i].add(new Object()); + } + } + System.gc(); + r.stop(); + List events = Events.fromRecording(r); + Events.hasEvents(events); + if (OldObjects.countChains(events) >= 30) { + return; + } + System.out.println("Not enough chains found, retrying."); + } + count--; + leak = null; + } + } +} diff --git a/test/jdk/jdk/jfr/event/runtime/TestThrowableInstrumentation.java b/test/jdk/jdk/jfr/event/runtime/TestThrowableInstrumentation.java index e1e135ab0b7..63d5dd16da9 100644 --- a/test/jdk/jdk/jfr/event/runtime/TestThrowableInstrumentation.java +++ b/test/jdk/jdk/jfr/event/runtime/TestThrowableInstrumentation.java @@ -52,7 +52,7 @@ public class TestThrowableInstrumentation { public static void main(String[] args) { // Compile Throwable:: with C1 (if available) if (!WHITE_BOX.enqueueInitializerForCompilation(java.lang.Throwable.class, COMP_LEVEL_SIMPLE)) { - if (!Platform.isServer() || isTieredCompilationEnabled() || Platform.isEmulatedClient()) { + if (!Platform.isServer() || isTieredCompilationEnabled()) { throw new RuntimeException("Unable to compile Throwable:: with C1"); } } diff --git a/test/jdk/jdk/jfr/event/runtime/TestVMInfoEvent.java b/test/jdk/jdk/jfr/event/runtime/TestVMInfoEvent.java index 8cbc6ea2bb8..ebee42b52df 100644 --- a/test/jdk/jdk/jfr/event/runtime/TestVMInfoEvent.java +++ b/test/jdk/jdk/jfr/event/runtime/TestVMInfoEvent.java @@ -40,6 +40,7 @@ import jdk.test.lib.jfr.Events; /** * @test * @modules java.base/jdk.internal.vm + * @modules java.management * @requires vm.flagless * @requires vm.gc == "Serial" | vm.gc == null * @requires vm.hasJFR diff --git a/test/jdk/jdk/jfr/jvm/TestJFRIntrinsic.java b/test/jdk/jdk/jfr/jvm/TestJFRIntrinsic.java index 75ed0d14ca5..502eefd5375 100644 --- a/test/jdk/jdk/jfr/jvm/TestJFRIntrinsic.java +++ b/test/jdk/jdk/jfr/jvm/TestJFRIntrinsic.java @@ -131,10 +131,10 @@ public class TestJFRIntrinsic { int maxLevel = flagValue.intValue(); return IntStream.rangeClosed(1, maxLevel).toArray(); } else { - if (Platform.isServer() && !Platform.isEmulatedClient()) { + if (Platform.isServer()) { return new int[]{4}; } - if (Platform.isClient() || Platform.isMinimal() || Platform.isEmulatedClient()) { + if (Platform.isClient() || Platform.isMinimal()) { return new int[]{1}; } } diff --git a/test/jdk/jdk/nio/Basic.java b/test/jdk/jdk/nio/Basic.java index 02fcfbc4a4d..68f8f00e309 100644 --- a/test/jdk/jdk/nio/Basic.java +++ b/test/jdk/jdk/nio/Basic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -25,7 +25,7 @@ * @test * @bug 8198372 * @modules jdk.net java.base/sun.nio.ch:+open - * @run testng Basic + * @run junit Basic * @summary Basic tests for jdk.nio.Channels */ @@ -48,10 +48,14 @@ import jdk.nio.Channels.SelectableChannelCloser; import sun.nio.ch.IOUtil; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; -@Test public class Basic { /** @@ -119,6 +123,7 @@ public class Basic { /** * Basic test of channel registered with Selector */ + @Test public void testSelect() throws IOException { Selector sel = Selector.open(); try (Connection connection = Connection.open()) { @@ -131,7 +136,7 @@ public class Basic { ch.configureBlocking(false); SelectionKey key = ch.register(sel, SelectionKey.OP_READ); int n = sel.selectNow(); - assertTrue(n == 0); + assertEquals(0, n); // write bytes to other end of connection SocketChannel peer = connection.channel2(); @@ -141,7 +146,7 @@ public class Basic { // channel should be selected n = sel.select(); - assertTrue(n == 1); + assertEquals(1, n); assertTrue(sel.selectedKeys().contains(key)); assertTrue(key.isReadable()); assertFalse(key.isWritable()); @@ -150,7 +155,7 @@ public class Basic { // change interest set for writing, channel should be selected key.interestOps(SelectionKey.OP_WRITE); n = sel.select(); - assertTrue(n == 1); + assertEquals(1, n); assertTrue(sel.selectedKeys().contains(key)); assertTrue(key.isWritable()); assertFalse(key.isReadable()); @@ -159,7 +164,7 @@ public class Basic { // change interest set for reading + writing, channel should be selected key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); n = sel.select(); - assertTrue(n == 1); + assertEquals(1, n); assertTrue(sel.selectedKeys().contains(key)); assertTrue(key.isWritable()); assertTrue(key.isReadable()); @@ -168,7 +173,7 @@ public class Basic { // change interest set to 0 to deregister, channel should not be selected key.interestOps(0); n = sel.selectNow(); - assertTrue(n == 0); + assertEquals(0, n); } finally { sel.close(); @@ -178,6 +183,7 @@ public class Basic { /** * Test that the SelectableChannelCloser implCloseChannel method is invoked. */ + @Test public void testImplCloseChannel() throws IOException { try (Connection connection = Connection.open()) { FileDescriptor fd = getFD(connection.channel1()); @@ -189,11 +195,11 @@ public class Basic { ch.close(); // implCloseChannel should been invoked once - assertTrue(closer.closeCount == 1); - assertTrue(closer.invokedToClose == ch); + assertEquals(1, closer.closeCount); + assertSame(ch, closer.invokedToClose); // implReleaseChannel should not have been invoked - assertTrue(closer.releaseCount == 0); + assertEquals(0, closer.releaseCount); } } } @@ -201,6 +207,7 @@ public class Basic { /** * Test that the SelectableChannelCloser implReleaseChannel method is invoked. */ + @Test public void testImplReleaseChannel() throws IOException { Selector sel = Selector.open(); try (Connection connection = Connection.open()) { @@ -217,50 +224,53 @@ public class Basic { ch.close(); // implCloseChannel should have been invoked - assertTrue(closer.closeCount == 1); - assertTrue(closer.invokedToClose == ch); + assertEquals(1, closer.closeCount); + assertSame(ch, closer.invokedToClose); // implReleaseChannel should not have been invoked - assertTrue(closer.releaseCount == 0); + assertEquals(0, closer.releaseCount); // flush the selector sel.selectNow(); // implReleaseChannel should have been invoked - assertTrue(closer.releaseCount == 1); - assertTrue(closer.invokedToRelease == ch); + assertEquals(1, closer.releaseCount); + assertSame(ch, closer.invokedToRelease); } finally { sel.close(); } } - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testInvalidFileDescriptor() throws IOException { FileDescriptor fd = IOUtil.newFD(-1); - Channels.readWriteSelectableChannel(fd, new SelectableChannelCloser() { - @Override - public void implCloseChannel(SelectableChannel sc) { } - @Override - public void implReleaseChannel(SelectableChannel sc) { } - }); + assertThrows + (IllegalArgumentException.class, + () -> Channels.readWriteSelectableChannel(fd, new SelectableChannelCloser() { + @Override + public void implCloseChannel(SelectableChannel sc) { } + @Override + public void implReleaseChannel(SelectableChannel sc) { }})); } - @Test(expectedExceptions = NullPointerException.class) + @Test public void testNullFileDescriptor() throws IOException { - Channels.readWriteSelectableChannel(null, new SelectableChannelCloser() { - @Override - public void implCloseChannel(SelectableChannel sc) { } - @Override - public void implReleaseChannel(SelectableChannel sc) { } - }); + assertThrows + (NullPointerException.class, + () -> Channels.readWriteSelectableChannel(null, new SelectableChannelCloser() { + @Override + public void implCloseChannel(SelectableChannel sc) { } + @Override + public void implReleaseChannel(SelectableChannel sc) { }})); } - @Test(expectedExceptions = NullPointerException.class) + @Test public void testNullCloser() throws IOException { try (Connection connection = Connection.open()) { FileDescriptor fd = getFD(connection.channel1()); - Channels.readWriteSelectableChannel(fd, null); + assertThrows(NullPointerException.class, + () -> Channels.readWriteSelectableChannel(fd, null)); } } @@ -271,7 +281,8 @@ public class Basic { f.setAccessible(true); return (FileDescriptor) f.get(sc); } catch (Exception e) { - throw new Error(e); + fail(e); + return null; // appease compiler } } } diff --git a/test/jdk/jdk/nio/zipfs/Basic.java b/test/jdk/jdk/nio/zipfs/Basic.java index faaf634f93c..8416145299a 100644 --- a/test/jdk/jdk/nio/zipfs/Basic.java +++ b/test/jdk/jdk/nio/zipfs/Basic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, 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 @@ -21,15 +21,18 @@ * questions. */ +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.net.URISyntaxException; import java.nio.file.AccessMode; import java.nio.file.ClosedFileSystemException; import java.nio.file.FileStore; -import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.PathMatcher; import java.nio.file.ProviderMismatchException; import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardCopyOption; @@ -37,103 +40,135 @@ import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.spi.FileSystemProvider; import java.net.URI; import java.io.IOException; -import java.util.Collections; import java.util.Map; + import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; -/** +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/* * @test * @bug 8038500 8040059 8150366 8150496 8147539 8290047 * @summary Basic test for zip provider - * * @modules jdk.zipfs - * @run main Basic + * @run junit Basic */ - public class Basic { - public static void main(String[] args) throws Exception { - // Test: zip should be returned in provider list - boolean found = false; - for (FileSystemProvider provider: FileSystemProvider.installedProviders()) { - if (provider.getScheme().equalsIgnoreCase("jar")) { - found = true; - break; - } - } - if (!found) - throw new RuntimeException("'jar' provider not installed"); - // create JAR file for test - Path jarFile = Utils.createJarFile("basic.jar", + static Path jarFile; + static URI uri; + + @BeforeAll + static void setup() throws IOException, URISyntaxException { + jarFile = Utils.createJarFile("basic.jar", "META-INF/services/java.nio.file.spi.FileSystemProvider"); + uri = new URI("jar", jarFile.toUri().toString(), null); + } + @AfterAll + static void cleanUp() throws IOException { + Files.deleteIfExists(jarFile); + } + + @Test + void providerListTest() { + // Test: zip should be returned in provider list + assertTrue(FileSystemProvider.installedProviders().stream() + .anyMatch(p -> p.getScheme().equalsIgnoreCase("jar")), + "'jar' provider not installed"); + } + + @Test + void newFileSystemTest() throws IOException { + // To test `newFileSystem`, close the shared FileSystem + var fs = FileSystems.newFileSystem(uri, Map.of()); + fs.close(); // Test: FileSystems#newFileSystem(Path) - Map env = Collections.emptyMap(); FileSystems.newFileSystem(jarFile).close(); - // Test: FileSystems#newFileSystem(URI) - URI uri = new URI("jar", jarFile.toUri().toString(), null); - FileSystem fs = FileSystems.newFileSystem(uri, env, null); + FileSystems.newFileSystem(uri, Map.of()).close(); + } - // Test: exercise toUri method - String expected = uri.toString() + "!/foo"; - String actual = fs.getPath("/foo").toUri().toString(); - if (!actual.equals(expected)) { - throw new RuntimeException("toUri returned '" + actual + - "', expected '" + expected + "'"); + @Test + void toUriTest() throws IOException { + try (var fs = FileSystems.newFileSystem(uri, Map.of())) { + // Test: exercise toUri method + String expected = uri.toString() + "!/foo"; + String actual = fs.getPath("/foo").toUri().toString(); + assertEquals(expected, actual, "toUri returned '" + actual + + "', expected '" + expected + "'"); } + } - // Test: exercise directory iterator and retrieval of basic attributes - Files.walkFileTree(fs.getPath("/"), new FileTreePrinter()); + @Test + void directoryIteratorTest() throws IOException { + try (var fs = FileSystems.newFileSystem(uri, Map.of())) { + // Test: exercise directory iterator and retrieval of basic attributes + Files.walkFileTree(fs.getPath("/"), new FileTreePrinter()); + } + } - // Test: copy file from zip file to current (scratch) directory - Path source = fs.getPath("/META-INF/services/java.nio.file.spi.FileSystemProvider"); - if (Files.exists(source)) { - Path target = Path.of(source.getFileName().toString()); - Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); - try { - long s1 = Files.readAttributes(source, BasicFileAttributes.class).size(); - long s2 = Files.readAttributes(target, BasicFileAttributes.class).size(); - if (s2 != s1) - throw new RuntimeException("target size != source size"); - } finally { - Files.delete(target); + @Test + void copyFileTest() throws IOException { + try (var fs = FileSystems.newFileSystem(uri, Map.of())) { + // Test: copy file from zip file to current (scratch) directory + Path source = fs.getPath("/META-INF/services/java.nio.file.spi.FileSystemProvider"); + if (Files.exists(source)) { + Path target = Path.of(source.getFileName().toString()); + Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); + try { + long s1 = Files.readAttributes(source, BasicFileAttributes.class).size(); + long s2 = Files.readAttributes(target, BasicFileAttributes.class).size(); + assertEquals(s1, s2, "target size != source size"); + } finally { + Files.delete(target); + } } } + } - // Test: FileStore - FileStore store = Files.getFileStore(fs.getPath("/")); - if (!store.supportsFileAttributeView("basic")) - throw new RuntimeException("BasicFileAttributeView should be supported"); - - // Test: watch register should throw PME - try { - fs.getPath("/") - .register(FileSystems.getDefault().newWatchService(), ENTRY_CREATE); - throw new RuntimeException("watch service is not supported"); - } catch (ProviderMismatchException x) { } - - // Test: IllegalArgumentException - try { - PathMatcher pm = fs.getPathMatcher(":glob"); - throw new RuntimeException("IllegalArgumentException not thrown"); - } catch (IllegalArgumentException iae) { - } - try { - PathMatcher pm = fs.getPathMatcher("glob:"); - } catch (IllegalArgumentException iae) { - iae.printStackTrace(); - throw new RuntimeException("Unexpected IllegalArgumentException"); + @Test + void fileStoreTest() throws IOException { + try (var fs = FileSystems.newFileSystem(uri, Map.of())) { + // Test: FileStore + FileStore store = Files.getFileStore(fs.getPath("/")); + assertTrue(store.supportsFileAttributeView("basic"), + "BasicFileAttributeView should be supported"); } + } + @Test + void watchRegisterNPETest() throws IOException { + try (var fs = FileSystems.newFileSystem(uri, Map.of())) { + // Test: watch register should throw PME + assertThrows(ProviderMismatchException.class, () -> fs.getPath("/") + .register(FileSystems.getDefault().newWatchService(), ENTRY_CREATE), + "watch service is not supported"); + } + } + + @Test + void pathMatcherIAETest() throws IOException { + try (var fs = FileSystems.newFileSystem(uri, Map.of())) { + // Test: IllegalArgumentException + assertThrows(IllegalArgumentException.class, () -> fs.getPathMatcher(":glob"), + "IllegalArgumentException not thrown"); + assertDoesNotThrow(() -> fs.getPathMatcher("glob:"), + "Unexpected IllegalArgumentException"); + } + } + + @Test + void closedFileSystemTest() throws IOException { // Test: ClosedFileSystemException + var fs = FileSystems.newFileSystem(uri, Map.of()); fs.close(); - if (fs.isOpen()) - throw new RuntimeException("FileSystem should be closed"); - try { - fs.provider().checkAccess(fs.getPath("/missing"), AccessMode.READ); - } catch (ClosedFileSystemException x) { } - - Files.deleteIfExists(jarFile); + assertFalse(fs.isOpen(), "FileSystem should be closed"); + assertThrows(ClosedFileSystemException.class, + () -> fs.provider().checkAccess(fs.getPath("/missing"), AccessMode.READ)); } // FileVisitor that pretty prints a file tree diff --git a/test/jdk/jdk/nio/zipfs/CRCWriteTest.java b/test/jdk/jdk/nio/zipfs/CRCWriteTest.java index 3f333ae9a44..268f8f42faf 100644 --- a/test/jdk/jdk/nio/zipfs/CRCWriteTest.java +++ b/test/jdk/jdk/nio/zipfs/CRCWriteTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -21,7 +21,6 @@ * questions. * */ -import org.testng.annotations.Test; import java.io.FileInputStream; import java.io.IOException; @@ -32,20 +31,20 @@ import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /** * @test * @bug 8232879 * @summary Test OutputStream::write with Zip FS * @modules jdk.zipfs - * @run testng/othervm CRCWriteTest + * @run junit/othervm CRCWriteTest */ public class CRCWriteTest { @@ -114,16 +113,16 @@ public class CRCWriteTest { // check entries with ZipFile API try (ZipFile zf = new ZipFile(zipfile.toFile())) { // check entry count - assertEquals(entries.length, zf.size()); + assertEquals(zf.size(), entries.length); // Check compression method and content of each entry for (Entry e : entries) { ZipEntry ze = zf.getEntry(e.name); assertNotNull(ze); - assertEquals(e.method, ze.getMethod()); + assertEquals(ze.getMethod(), e.method); try (InputStream in = zf.getInputStream(ze)) { byte[] bytes = in.readAllBytes(); - assertTrue(Arrays.equals(bytes, e.bytes)); + assertArrayEquals(bytes, e.bytes); } } } @@ -134,9 +133,9 @@ public class CRCWriteTest { new ZipInputStream(new FileInputStream(zipfile.toFile()))) { ZipEntry ze; while ((ze = zis.getNextEntry()) != null) { - assertEquals(e.method, ze.getMethod()); + assertEquals(ze.getMethod(), e.method); byte[] bytes = zis.readAllBytes(); - assertTrue(Arrays.equals(bytes, e.bytes)); + assertArrayEquals(bytes, e.bytes); } } } @@ -147,13 +146,13 @@ public class CRCWriteTest { Path top = fs.getPath("/"); long count = Files.find(top, Integer.MAX_VALUE, (path, attrs) -> attrs.isRegularFile()).count(); - assertEquals(entries.length, count); + assertEquals(count, entries.length); // check content of each entry for (Entry e : entries) { Path file = fs.getPath(e.name); byte[] bytes = Files.readAllBytes(file); - assertTrue(Arrays.equals(bytes, e.bytes)); + assertArrayEquals(bytes, e.bytes); } } } diff --git a/test/jdk/jdk/nio/zipfs/testng/test/ChannelTests.java b/test/jdk/jdk/nio/zipfs/ChannelTests.java similarity index 93% rename from test/jdk/jdk/nio/zipfs/testng/test/ChannelTests.java rename to test/jdk/jdk/nio/zipfs/ChannelTests.java index 3c7d18988b7..ffeed251649 100644 --- a/test/jdk/jdk/nio/zipfs/testng/test/ChannelTests.java +++ b/test/jdk/jdk/nio/zipfs/ChannelTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -21,9 +21,9 @@ * questions. * */ -package test; -import org.testng.annotations.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import util.ZipFsBaseTest; import java.io.ByteArrayOutputStream; @@ -33,7 +33,11 @@ import java.nio.channels.ClosedChannelException; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.SeekableByteChannel; -import java.nio.file.*; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; import java.util.Map; import java.util.Random; @@ -41,15 +45,24 @@ import java.util.Set; import java.util.zip.ZipEntry; import static java.nio.charset.StandardCharsets.UTF_8; -import static java.nio.file.StandardOpenOption.*; -import static org.testng.Assert.*; +import static java.nio.file.StandardOpenOption.APPEND; +import static java.nio.file.StandardOpenOption.CREATE; +import static java.nio.file.StandardOpenOption.CREATE_NEW; +import static java.nio.file.StandardOpenOption.READ; +import static java.nio.file.StandardOpenOption.WRITE; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; -/** +/* * @test * @bug 8242006 * @summary Improve FileChannel and SeekableByteChannel Zip FS test coverage * @modules jdk.zipfs - * @run testng test.ChannelTests + * @run junit ChannelTests */ public class ChannelTests extends ZipFsBaseTest { @@ -81,7 +94,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcFromOSToZipTest(final Map env, final int compression) throws Exception { Entry e00 = Entry.of("Entry-00", compression, FIFTH_MAJOR); @@ -108,7 +122,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcFromZipToOSTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -125,7 +140,7 @@ public class ChannelTests extends ZipFsBaseTest { } // Check to see if the file exists and the bytes match assertTrue(Files.isRegularFile(osFile)); - assertEquals(Files.readAllBytes(osFile), e0.bytes); + assertArrayEquals(e0.bytes, Files.readAllBytes(osFile)); Files.deleteIfExists(zipFile); Files.deleteIfExists(osFile); } @@ -138,7 +153,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcFromZipToZipTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -170,7 +186,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param expectedCompression The compression to be used when copying the entry * @throws Exception If an error occurs */ - @Test(dataProvider = "copyMoveMap") + @ParameterizedTest + @MethodSource("copyMoveMap") public void sbcChangeCompressionTest(final Map env, final int compression, final int expectedCompression) throws Exception { @@ -206,7 +223,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcReadTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -222,7 +240,7 @@ public class ChannelTests extends ZipFsBaseTest { int bytesRead = sbc.read(buf); // Check to see if the expected bytes were read byte[] result = Arrays.copyOfRange(buf.array(), 0, bytesRead); - assertEquals(THE_SLAMS.getBytes(UTF_8), result); + assertArrayEquals(THE_SLAMS.getBytes(UTF_8), result); } Files.deleteIfExists(zipFile); } @@ -234,7 +252,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcWriteTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -261,7 +280,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcAppendTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -291,7 +311,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcTruncateTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -317,7 +338,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcFAETest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -344,7 +366,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcCloseTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -369,7 +392,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcCCETest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -401,7 +425,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcSizeTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -412,17 +437,17 @@ public class ChannelTests extends ZipFsBaseTest { try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env); SeekableByteChannel sbc = Files.newByteChannel( zipfs.getPath(e0.name), Set.of(READ))) { - assertEquals(sbc.size(), THE_SLAMS.length()); + assertEquals(THE_SLAMS.length(), sbc.size()); } try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env); SeekableByteChannel sbc = Files.newByteChannel(zipfs.getPath(e0.name))) { - assertEquals(sbc.size(), THE_SLAMS.length()); + assertEquals(THE_SLAMS.length(), sbc.size()); } try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env); SeekableByteChannel sbc = Files.newByteChannel(zipfs.getPath("Entry-01") , Set.of(CREATE, WRITE))) { sbc.write(ByteBuffer.wrap(FIFTH_MAJOR.getBytes(UTF_8))); - assertEquals(sbc.size(), FIFTH_MAJOR.length()); + assertEquals(FIFTH_MAJOR.length(), sbc.size()); } Files.deleteIfExists(zipFile); } @@ -435,7 +460,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcOpenClosedTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -462,7 +488,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcPositionTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -480,7 +507,7 @@ public class ChannelTests extends ZipFsBaseTest { for (var i = 0; i < fSize; i++) { long pos = RANDOM.nextInt(seed); sbc.position(pos); - assertEquals(sbc.position(), pos); + assertEquals(pos, sbc.position()); } } Files.deleteIfExists(zipFile); @@ -496,7 +523,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcFromOSToZipTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -522,7 +550,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcFromZipToOSTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -538,7 +567,7 @@ public class ChannelTests extends ZipFsBaseTest { } // Check to see if the file exists and the bytes match assertTrue(Files.isRegularFile(osFile)); - assertEquals(Files.readAllBytes(osFile), e0.bytes); + assertArrayEquals(e0.bytes, Files.readAllBytes(osFile)); Files.deleteIfExists(zipFile); Files.deleteIfExists(osFile); } @@ -551,7 +580,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcFromZipToZipTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -583,7 +613,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param expectedCompression The compression to be used when copying the entry * @throws Exception If an error occurs */ - @Test(dataProvider = "copyMoveMap") + @ParameterizedTest + @MethodSource("copyMoveMap") public void fcChangeCompressionTest(final Map env, final int compression, final int expectedCompression) throws Exception { @@ -620,7 +651,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcAppendTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -647,7 +679,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcTruncateTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -672,7 +705,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcMapTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -701,7 +735,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcOpenClosedTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -728,7 +763,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcFAETest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -752,7 +788,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcCloseTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -777,7 +814,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcCCETest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -839,7 +877,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcReadTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -854,7 +893,7 @@ public class ChannelTests extends ZipFsBaseTest { int bytesRead = fc.read(buf); // Check to see if the expected bytes were read byte[] result = Arrays.copyOfRange(buf.array(), 0, bytesRead); - assertEquals(THE_SLAMS.getBytes(UTF_8), result); + assertArrayEquals(THE_SLAMS.getBytes(UTF_8), result); } Files.deleteIfExists(zipFile); } @@ -867,7 +906,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcReadPosTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -882,7 +922,7 @@ public class ChannelTests extends ZipFsBaseTest { int bytesRead = fc.read(buf, GRAND_SLAMS_HEADER.length()); // Check to see if the expected bytes were read byte[] result = Arrays.copyOfRange(buf.array(), 0, bytesRead); - assertEquals(GRAND_SLAMS.getBytes(UTF_8), result); + assertArrayEquals(GRAND_SLAMS.getBytes(UTF_8), result); } Files.deleteIfExists(zipFile); } @@ -895,7 +935,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcReadArrayTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -921,7 +962,7 @@ public class ChannelTests extends ZipFsBaseTest { bos.write(b.array()); } // Check to see if the returned byte array is what is expected - assertEquals(e0.bytes, bos.toByteArray()); + assertArrayEquals(bos.toByteArray(), e0.bytes); } Files.deleteIfExists(zipFile); } @@ -934,7 +975,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcReadArrayWithOffsetTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -969,7 +1011,7 @@ public class ChannelTests extends ZipFsBaseTest { bos.write(b.array()); } // Check to see if the returned byte array is what is expected - assertEquals(GRAND_SLAMS.getBytes(UTF_8), bos.toByteArray()); + assertArrayEquals(GRAND_SLAMS.getBytes(UTF_8), bos.toByteArray()); Files.deleteIfExists(zipFile); } @@ -981,7 +1023,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcTransferToZipTest(final Map env, final int compression) throws Exception { Entry e00 = Entry.of("Entry-00", compression, THE_SLAMS); @@ -995,7 +1038,7 @@ public class ChannelTests extends ZipFsBaseTest { } // Verify the entry was copied verify(zipFile, e00); - assertEquals(Files.readAllBytes(osFile), e00.bytes); + assertArrayEquals(e00.bytes, Files.readAllBytes(osFile)); Files.deleteIfExists(osFile); Files.deleteIfExists(zipFile); } @@ -1008,7 +1051,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcTransferToOsTest(final Map env, final int compression) throws Exception { Entry e00 = Entry.of("Entry-00", compression, THE_SLAMS); @@ -1021,7 +1065,7 @@ public class ChannelTests extends ZipFsBaseTest { fcTransferTo(zipfs.getPath(e00.name), osFile); } // Verify the entry was copied - assertEquals(Files.readAllBytes(osFile), e00.bytes); + assertArrayEquals(e00.bytes, Files.readAllBytes(osFile)); Files.deleteIfExists(osFile); Files.deleteIfExists(zipFile); } @@ -1034,7 +1078,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcTransferToZipToZipTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -1065,7 +1110,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcTransferFromOsTest(final Map env, final int compression) throws Exception { Entry e00 = Entry.of("Entry-00", compression, THE_SLAMS); @@ -1079,7 +1125,7 @@ public class ChannelTests extends ZipFsBaseTest { } // Verify the entry was copied zip(zipFile, env, e00); - assertEquals(Files.readAllBytes(osFile), e00.bytes); + assertArrayEquals(e00.bytes, Files.readAllBytes(osFile)); Files.deleteIfExists(osFile); Files.deleteIfExists(zipFile); } @@ -1092,7 +1138,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcTransferFromZipTest(final Map env, final int compression) throws Exception { Entry e00 = Entry.of("Entry-00", compression, THE_SLAMS); @@ -1105,7 +1152,7 @@ public class ChannelTests extends ZipFsBaseTest { fcTransferFrom(zipfs.getPath(e00.name), osFile); } // Verify the bytes match - assertEquals(Files.readAllBytes(osFile), e00.bytes); + assertArrayEquals(e00.bytes, Files.readAllBytes(osFile)); Files.deleteIfExists(osFile); Files.deleteIfExists(zipFile); } @@ -1118,7 +1165,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcTransferFromZipToZipTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -1148,7 +1196,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcWriteTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -1174,7 +1223,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcWritePosTest(final Map env, final int compression) throws Exception { // Use this value to replace the value specified for AUSTRALIAN_OPEN @@ -1209,7 +1259,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcWriteArrayTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -1227,7 +1278,7 @@ public class ChannelTests extends ZipFsBaseTest { FileChannel fc = FileChannel.open(zipfs.getPath(e0.name), Set.of(CREATE, WRITE))) { fc.write(bb); - assertEquals(fc.size(), GRAND_SLAMS.length()); + assertEquals(GRAND_SLAMS.length(), fc.size()); } // Verify the entry was created verify(zipFile, e0); @@ -1242,7 +1293,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcWriteArrayWithOffsetTest(final Map env, final int compression) throws Exception { @@ -1276,7 +1328,7 @@ public class ChannelTests extends ZipFsBaseTest { fc.position(GRAND_SLAMS_HEADER.length()); // Replace the original values fc.write(bb, 0, 2); - assertEquals(fc.size(), THE_SLAMS.length()); + assertEquals(THE_SLAMS.length(), fc.size()); } // Verify the entry was updated verify(zipFile, e0.content(updatedFile)); @@ -1290,7 +1342,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcForceWriteTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -1317,7 +1370,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcPositionTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -1334,7 +1388,7 @@ public class ChannelTests extends ZipFsBaseTest { for (var i = 0; i < fSize; i++) { long pos = RANDOM.nextInt(seed); fc.position(pos); - assertEquals(fc.position(), pos); + assertEquals(pos, fc.position()); } } Files.deleteIfExists(zipFile); @@ -1347,7 +1401,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcSizeTest(final Map env, final int compression) throws Exception { Path osFile = Path.of("GrandSlams.txt"); @@ -1360,29 +1415,29 @@ public class ChannelTests extends ZipFsBaseTest { // Validate the file sizes match try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env); FileChannel fc = FileChannel.open(zipfs.getPath(e0.name))) { - assertEquals(fc.size(), THE_SLAMS.length()); + assertEquals(THE_SLAMS.length(), fc.size()); } try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env); FileChannel fc = FileChannel.open(zipfs.getPath(e0.name), Set.of(READ))) { - assertEquals(fc.size(), THE_SLAMS.length()); + assertEquals(THE_SLAMS.length(), fc.size()); } try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env); FileChannel fc = FileChannel.open(zipfs.getPath(e0.name), Set.of(READ, WRITE))) { - assertEquals(fc.size(), THE_SLAMS.length()); + assertEquals(THE_SLAMS.length(), fc.size()); } try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env); FileChannel fc = FileChannel.open(zipfs.getPath(e0.name), Set.of(WRITE))) { - assertEquals(fc.size(), THE_SLAMS.length()); + assertEquals(THE_SLAMS.length(), fc.size()); } try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env); FileChannel fc = FileChannel.open(zipfs.getPath(e0.name), Set.of(APPEND))) { - assertEquals(fc.size(), THE_SLAMS.length()); + assertEquals(THE_SLAMS.length(), fc.size()); } try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env); FileChannel fc = FileChannel.open(zipfs.getPath("Entry-01"), Set.of(CREATE, WRITE))) { fc.write(ByteBuffer.wrap(FIFTH_MAJOR.getBytes(UTF_8))); - assertEquals(fc.size(), FIFTH_MAJOR.length()); + assertEquals(FIFTH_MAJOR.length(), fc.size()); } Files.deleteIfExists(zipFile); Files.deleteIfExists(osFile); @@ -1395,7 +1450,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcLockTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -1426,7 +1482,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcTryLockTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); diff --git a/test/jdk/jdk/nio/zipfs/CompressionModeTest.java b/test/jdk/jdk/nio/zipfs/CompressionModeTest.java index 1b024d4addf..1307f6f4ed7 100644 --- a/test/jdk/jdk/nio/zipfs/CompressionModeTest.java +++ b/test/jdk/jdk/nio/zipfs/CompressionModeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -22,7 +22,6 @@ * */ -import org.testng.annotations.*; import java.io.IOException; import java.io.InputStream; @@ -32,22 +31,27 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.security.SecureRandom; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import static java.lang.String.format; import static java.util.stream.Collectors.joining; -import static org.testng.Assert.*; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; /** * @test * @bug 8231093 * @summary Test Zip FS compressionMethod property * @modules jdk.zipfs - * @run testng CompressionModeTest + * @run junit CompressionModeTest */ public class CompressionModeTest { @@ -77,7 +81,8 @@ public class CompressionModeTest { * @throws Exception If an error occurs during the creation, verification or * deletion of the ZIP file */ - @Test(dataProvider = "validCompressionMethods", enabled = true) + @ParameterizedTest + @MethodSource("validCompressionMethods") public void testValidCompressionMehods(Map env, int compression) throws Exception { @@ -99,7 +104,8 @@ public class CompressionModeTest { * @throws Exception if an error occurs other than the expected * IllegalArgumentException */ - @Test(dataProvider = "invalidCompressionMethod") + @ParameterizedTest + @MethodSource("invalidCompressionMethod") public void testInvalidCompressionMethod(Map env) throws Exception { System.out.printf("ZIP FS Map = %s%n ", formatMap(env)); Path zipfile = generatePath(HERE, "test", ".zip"); @@ -131,44 +137,42 @@ public class CompressionModeTest { } /** - * DataProvider used to validate that you can create a ZIP file with and + * MethodSource used to validate that you can create a ZIP file with and * without compression. */ - @DataProvider(name = "validCompressionMethods") - private Object[][] validCompressionMethods() { - return new Object[][]{ - {Map.of("create", "true"), ZipEntry.DEFLATED}, - {Map.of("create", "true", "noCompression", "true"), - ZipEntry.STORED}, - {Map.of("create", "true", "noCompression", "false"), - ZipEntry.DEFLATED}, - {Map.of("create", "true", "compressionMethod", "STORED"), - ZipEntry.STORED}, - {Map.of("create", "true", "compressionMethod", "DEFLATED"), - ZipEntry.DEFLATED}, - {Map.of("create", "true", "compressionMethod", "stored"), - ZipEntry.STORED}, - {Map.of("create", "true", "compressionMethod", "deflated"), - ZipEntry.DEFLATED} - }; + private static Stream validCompressionMethods() { + return Stream.of( + Arguments.of(Map.of("create", "true"), ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "noCompression", "true"), + ZipEntry.STORED), + Arguments.of(Map.of("create", "true", "noCompression", "false"), + ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "compressionMethod", "STORED"), + ZipEntry.STORED), + Arguments.of(Map.of("create", "true", "compressionMethod", "DEFLATED"), + ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "compressionMethod", "stored"), + ZipEntry.STORED), + Arguments.of(Map.of("create", "true", "compressionMethod", "deflated"), + ZipEntry.DEFLATED) + ); } /** - * DataProvider used to validate that an IllegalArgumentException is thrown + * MethodSource used to validate that an IllegalArgumentException is thrown * for an invalid value for the compressionMethod property. */ - @DataProvider(name = "invalidCompressionMethod") - private Object[][] invalidCompressionMethod() { + private static Stream invalidCompressionMethod() { HashMap map = new HashMap<>(); map.put("create", "true"); map.put("compressionMethod", null); - return new Object[][]{ - {map}, - {Map.of("create", "true", "compressionMethod", "")}, - {Map.of("create", "true", "compressionMethod", - Integer.parseInt("5"))}, - {Map.of("create", "true", "compressionMethod", "invalid")} - }; + return Stream.of( + Arguments.of(map), + Arguments.of(Map.of("create", "true", "compressionMethod", "")), + Arguments.of(Map.of("create", "true", "compressionMethod", + Integer.parseInt("5"))), + Arguments.of(Map.of("create", "true", "compressionMethod", "invalid")) + ); } /** @@ -186,16 +190,16 @@ public class CompressionModeTest { // check entries with ZIP API try (ZipFile zf = new ZipFile(zipfile.toFile())) { // check entry count - assertEquals(entries, zf.size()); + assertEquals(zf.size(), entries); // check compression method and content of each entry for (int i = start; i < entries; i++) { ZipEntry ze = zf.getEntry("Entry-" + i); assertNotNull(ze); - assertEquals(method, ze.getMethod()); + assertEquals(ze.getMethod(), method); try (InputStream is = zf.getInputStream(ze)) { byte[] bytes = is.readAllBytes(); - assertTrue(Arrays.equals(bytes, ZIP_FILE_ENTRY)); + assertArrayEquals(ZIP_FILE_ENTRY, bytes); } } } @@ -209,13 +213,13 @@ public class CompressionModeTest { path.getFileName() != null && path.getFileName().toString().equals("META-INF"))) .count(); - assertEquals(entries, count); + assertEquals(count, entries); // check content of each entry for (int i = start; i < entries; i++) { Path file = fs.getPath("Entry-" + i); byte[] bytes = Files.readAllBytes(file); - assertTrue(Arrays.equals(bytes, ZIP_FILE_ENTRY)); + assertArrayEquals(ZIP_FILE_ENTRY, bytes); } } } diff --git a/test/jdk/jdk/nio/zipfs/CopyMoveTests.java b/test/jdk/jdk/nio/zipfs/CopyMoveTests.java index 1991528f521..fc941bfe056 100644 --- a/test/jdk/jdk/nio/zipfs/CopyMoveTests.java +++ b/test/jdk/jdk/nio/zipfs/CopyMoveTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -22,29 +22,32 @@ * */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.security.SecureRandom; -import java.util.Arrays; import java.util.Map; +import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import static java.lang.String.format; import static java.util.stream.Collectors.joining; -import static org.testng.Assert.*; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; /** * @test * @bug 8231766 * @summary Test Files::copy and Files::move with Zip FS * @modules jdk.zipfs - * @run testng/othervm CopyMoveTests + * @run junit/othervm CopyMoveTests */ public class CopyMoveTests { // Enable debugging output @@ -58,33 +61,31 @@ public class CopyMoveTests { private static final SecureRandom random = new SecureRandom(); /* - * DataProvider used to verify that a FileAlreadyExistsException is + * MethodSource used to verify that a FileAlreadyExistsException is * thrown with copying a file without the REPLACE_EXISTING option */ - @DataProvider(name = "zipfsMap") - private Object[][] zipfsMap() { - return new Object[][]{ - {Map.of("create", "true"), ZipEntry.DEFLATED}, - {Map.of("create", "true", "noCompression", "true"), - ZipEntry.STORED}, - {Map.of("create", "true", "noCompression", "false"), - ZipEntry.DEFLATED} - }; + private static Stream zipfsMap() { + return Stream.of( + Arguments.of(Map.of("create", "true"), ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "noCompression", "true"), + ZipEntry.STORED), + Arguments.of(Map.of("create", "true", "noCompression", "false"), + ZipEntry.DEFLATED) + ); } /* - * DataProvider used to verify that an entry may be copied or moved within + * MethodSource used to verify that an entry may be copied or moved within * a Zip file system with the correct compression method */ - @DataProvider(name = "copyMoveMap") - private Object[][] copyMoveMap() { - return new Object[][]{ - {Map.of("create", "true"), ZipEntry.DEFLATED, ZipEntry.STORED}, - {Map.of("create", "true", "noCompression", "true"), - ZipEntry.STORED, ZipEntry.DEFLATED}, - {Map.of("create", "true", "noCompression", "false"), - ZipEntry.DEFLATED, ZipEntry.STORED} - }; + private static Stream copyMoveMap() { + return Stream.of( + Arguments.of(Map.of("create", "true"), ZipEntry.DEFLATED, ZipEntry.STORED), + Arguments.of(Map.of("create", "true", "noCompression", "true"), + ZipEntry.STORED, ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "noCompression", "false"), + ZipEntry.DEFLATED, ZipEntry.STORED) + ); } /** @@ -96,7 +97,8 @@ public class CopyMoveTests { * @param expectedCompression The compression to be used when copying the entry * @throws Exception If an error occurs */ - @Test(dataProvider = "copyMoveMap", enabled = true) + @ParameterizedTest + @MethodSource("copyMoveMap") public void copyTest(Map createMap, int compression, int expectedCompression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE); @@ -127,7 +129,8 @@ public class CopyMoveTests { * @param expectedCompression The compression to be used when copying the entry * @throws Exception If an error occurs */ - @Test(dataProvider = "copyMoveMap", enabled = true) + @ParameterizedTest + @MethodSource("copyMoveMap") public void copyZipToZipTest(Map createMap, int compression, int expectedCompression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE); @@ -163,7 +166,8 @@ public class CopyMoveTests { * @param expectedCompression The compression to be used when copying the entry * @throws Exception If an error occurs */ - @Test(dataProvider = "copyMoveMap", enabled = true) + @ParameterizedTest + @MethodSource("copyMoveMap") public void copyFromOsTest(Map createMap, int compression, int expectedCompression) throws Exception { @@ -196,7 +200,8 @@ public class CopyMoveTests { * @param expectedCompression The compression to be used when moving the entry * @throws Exception If an error occurs */ - @Test(dataProvider = "copyMoveMap", enabled = true) + @ParameterizedTest + @MethodSource("copyMoveMap") public void CopyFromZipTest(Map createMap, int compression, int expectedCompression) throws Exception { @@ -217,7 +222,7 @@ public class CopyMoveTests { verify(zipFile, e0, e1); // Check to see if the file exists and the bytes match assertTrue(Files.isRegularFile(osFile)); - assertEquals(Files.readAllBytes(osFile), e0.bytes); + assertArrayEquals(e0.bytes, Files.readAllBytes(osFile)); Files.deleteIfExists(zipFile); Files.deleteIfExists(osFile); } @@ -231,7 +236,8 @@ public class CopyMoveTests { * @param expectedCompression The compression to be used when moving the entry * @throws Exception If an error occurs */ - @Test(dataProvider = "copyMoveMap", enabled = true) + @ParameterizedTest + @MethodSource("copyMoveMap") public void moveTest(Map createMap, int compression, int expectedCompression) throws Exception { @@ -261,7 +267,8 @@ public class CopyMoveTests { * @param expectedCompression The compression to be used when moving the entry * @throws Exception If an error occurs */ - @Test(dataProvider = "copyMoveMap", enabled = true) + @ParameterizedTest + @MethodSource("copyMoveMap") public void moveZipToZipTest(Map createMap, int compression, int expectedCompression) throws Exception { @@ -299,7 +306,8 @@ public class CopyMoveTests { * @param expectedCompression The compression to be used when moving the entry * @throws Exception If an error occurs */ - @Test(dataProvider = "copyMoveMap", enabled = true) + @ParameterizedTest + @MethodSource("copyMoveMap") public void moveFromZipTest(Map createMap, int compression, int expectedCompression) throws Exception { @@ -320,7 +328,7 @@ public class CopyMoveTests { verify(zipFile, e1); // Check to see if the file exists and the bytes match assertTrue(Files.isRegularFile(osFile)); - assertEquals(Files.readAllBytes(osFile), e0.bytes); + assertArrayEquals(e0.bytes, Files.readAllBytes(osFile)); Files.deleteIfExists(zipFile); Files.deleteIfExists(osFile); } @@ -332,7 +340,8 @@ public class CopyMoveTests { * @param createMap Properties used for creating the ZIP Filesystem * @throws Exception if an error occurs */ - @Test(dataProvider = "zipfsMap", enabled = true) + @ParameterizedTest + @MethodSource("zipfsMap") public void testFAEWithCopy(Map createMap, int compression) throws Exception { if (DEBUG) { @@ -444,17 +453,17 @@ public class CopyMoveTests { // check entries with zip API try (ZipFile zf = new ZipFile(zipfile.toFile())) { // check entry count - assertEquals(entries.length, zf.size()); + assertEquals(zf.size(), entries.length); // check compression method and content of each entry for (Entry e : entries) { ZipEntry ze = zf.getEntry(e.name); //System.out.printf("Name: %s, method: %s, Expected Method: %s%n", e.name, ze.getMethod(), e.method); assertNotNull(ze); - assertEquals(e.method, ze.getMethod()); + assertEquals(ze.getMethod(), e.method); try (InputStream in = zf.getInputStream(ze)) { byte[] bytes = in.readAllBytes(); - assertTrue(Arrays.equals(bytes, e.bytes)); + assertArrayEquals(bytes, e.bytes); } } } @@ -465,13 +474,13 @@ public class CopyMoveTests { Path top = fs.getPath("/"); long count = Files.find(top, Integer.MAX_VALUE, (path, attrs) -> attrs.isRegularFile()).count(); - assertEquals(entries.length, count); + assertEquals(count, entries.length); // check content of each entry for (Entry e : entries) { Path file = fs.getPath(e.name); byte[] bytes = Files.readAllBytes(file); - assertTrue(Arrays.equals(bytes, e.bytes)); + assertArrayEquals(bytes, e.bytes); } } } diff --git a/test/jdk/jdk/nio/zipfs/DirectoryStreamTests.java b/test/jdk/jdk/nio/zipfs/DirectoryStreamTests.java index 0a255caa812..6aaaa4d5cd7 100644 --- a/test/jdk/jdk/nio/zipfs/DirectoryStreamTests.java +++ b/test/jdk/jdk/nio/zipfs/DirectoryStreamTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -21,10 +21,6 @@ * questions. */ -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; import java.nio.file.*; @@ -33,9 +29,17 @@ import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.regex.PatternSyntaxException; +import java.util.stream.Stream; import java.util.zip.ZipException; -import static org.testng.Assert.*; +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; /** * @test @@ -43,7 +47,7 @@ import static org.testng.Assert.*; * @summary ZIP File System tests that leverage DirectoryStream * @modules jdk.zipfs * @compile DirectoryStreamTests.java - * @run testng DirectoryStreamTests + * @run junit DirectoryStreamTests */ public class DirectoryStreamTests { @@ -65,8 +69,8 @@ public class DirectoryStreamTests { /** * Create the JAR files used by the tests */ - @BeforeClass - public void setUp() throws Exception { + @BeforeAll + public static void setUp() throws Exception { emptyJarFile = Paths.get("emptyDir.jar"); try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(emptyJarFile, ZIPFS_MAP)) { @@ -80,8 +84,8 @@ public class DirectoryStreamTests { /** * Remove JAR files used by test as part of clean-up */ - @AfterClass - public void tearDown() throws Exception { + @AfterAll + public static void tearDown() throws Exception { Files.deleteIfExists(jarFile); Files.deleteIfExists(emptyJarFile); } @@ -91,7 +95,8 @@ public class DirectoryStreamTests { * System and that the returned Iterator correctly indicates whether the * filter has been matched */ - @Test(dataProvider = "filterTestValues") + @ParameterizedTest + @MethodSource("filterValues") public void test0000(String glob, boolean expectedResult, String errMsg) throws Exception { @@ -106,7 +111,7 @@ public class DirectoryStreamTests { } })) { - assertEquals(ds.iterator().hasNext(), expectedResult, errMsg); + assertEquals(expectedResult, ds.iterator().hasNext(), errMsg); } } @@ -114,7 +119,8 @@ public class DirectoryStreamTests { * Validate that you can specify a glob using the ZIP File System and that the * returned Iterator correctly indicates whether the glob pattern has been matched */ - @Test(dataProvider = "filterTestValues") + @ParameterizedTest + @MethodSource("filterValues") public void test0001(String glob, boolean expectedResult, String errMsg) throws Exception { @@ -122,7 +128,7 @@ public class DirectoryStreamTests { ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), UNZIPFS_MAP); DirectoryStream ds = Files.newDirectoryStream(zipfs.getPath("/"), glob)) { - assertEquals(ds.iterator().hasNext(), expectedResult, errMsg); + assertEquals(expectedResult, ds.iterator().hasNext(), errMsg); } } @@ -144,7 +150,8 @@ public class DirectoryStreamTests { * Validate that the correct type of paths are returned when creating a * DirectoryStream */ - @Test(dataProvider = "startPaths") + @ParameterizedTest + @MethodSource("Name") public void test0003(String startPath, String expectedPath) throws IOException { try (FileSystem zipfs = @@ -153,9 +160,8 @@ public class DirectoryStreamTests { Files.newDirectoryStream(zipfs.getPath(startPath))) { for (Path entry : stream) { - assertTrue(entry.toString().equals(expectedPath), - String.format("Error: Expected path %s not found when" - + " starting at %s%n", expectedPath, entry)); + assertEquals(entry.toString(), expectedPath, String.format("Error: Expected path %s not found when" + + " starting at %s%n", expectedPath, entry)); } } } @@ -310,35 +316,31 @@ public class DirectoryStreamTests { /** * Glob values to use to validate filtering */ - @DataProvider(name = "filterTestValues") - public static Object[][] filterValues() { + public static Stream filterValues() { String expectedMsg = "Error: Matching entries were expected but not found!!!"; String notExpectedMsg = "Error: No matching entries expected but were found!!!"; - return new Object[][]{ - - {"M*", true, expectedMsg}, - {"I*", false, notExpectedMsg} - }; + return Stream.of( + Arguments.of("M*", true, expectedMsg), + Arguments.of("I*", false, notExpectedMsg) + ); } /** * Starting Path for the DirectoryStream and the expected path to be returned * when traversing the stream */ - @DataProvider(name = "startPaths") - public static Object[][] Name() { - return new Object[][]{ - - {"META-INF", "META-INF/services"}, - {"/META-INF", "/META-INF/services"}, - {"/META-INF/../META-INF","/META-INF/../META-INF/services" }, - {"./META-INF", "./META-INF/services"}, - {"", "META-INF"}, - {"/", "/META-INF"}, - {".", "./META-INF"}, - {"./", "./META-INF"} - }; + public static Stream Name() { + return Stream.of( + Arguments.of("META-INF", "META-INF/services"), + Arguments.of("/META-INF", "/META-INF/services"), + Arguments.of("/META-INF/../META-INF", "/META-INF/../META-INF/services" ), + Arguments.of("./META-INF", "./META-INF/services"), + Arguments.of("", "META-INF"), + Arguments.of("/", "/META-INF"), + Arguments.of(".", "./META-INF"), + Arguments.of("./", "./META-INF") + ); } /** diff --git a/test/jdk/jdk/nio/zipfs/EndOfCenValidation.java b/test/jdk/jdk/nio/zipfs/EndOfCenValidation.java index ed0bdbb52ea..e8012cc3ea3 100644 --- a/test/jdk/jdk/nio/zipfs/EndOfCenValidation.java +++ b/test/jdk/jdk/nio/zipfs/EndOfCenValidation.java @@ -142,9 +142,8 @@ public class EndOfCenValidation { * @param msg exception message to expect */ private static void verifyRejection(Path zip, String msg) { - ZipException ex = assertThrows(ZipException.class, () -> { - FileSystems.newFileSystem(zip); - }); + ZipException ex = assertThrows(ZipException.class, + () -> FileSystems.newFileSystem(zip)); assertEquals(msg, ex.getMessage()); } } diff --git a/test/jdk/jdk/nio/zipfs/HasDotDotTest.java b/test/jdk/jdk/nio/zipfs/HasDotDotTest.java index 6c6111dd0ab..26a4a597d3a 100644 --- a/test/jdk/jdk/nio/zipfs/HasDotDotTest.java +++ b/test/jdk/jdk/nio/zipfs/HasDotDotTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -21,8 +21,6 @@ * questions. * */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -37,14 +35,20 @@ import java.util.zip.CRC32; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; /** * @test * @bug 8251329 * @summary Excercise Zip FS with "." or ".." in a Zip Entry name * @modules jdk.zipfs - * @run testng/othervm HasDotDotTest + * @run junit/othervm HasDotDotTest */ public class HasDotDotTest { // Zip file to be created @@ -56,22 +60,21 @@ public class HasDotDotTest { private static final boolean DEBUG = false; /** - * DataProvider containing Zip entry names which should result in an IOException + * MethodSource containing Zip entry names which should result in an IOException * @return Array of Zip entry names */ - @DataProvider - private Object[][] checkForDotOrDotDotPaths() { - return new Object[][]{ - {"/./foo"}, - {"/../foo"}, - {"/../foo/.."}, - {"/foo/.."}, - {"/foo/."}, - {"/.."}, - {"/."}, - {"/.foo/./"}, - {"/.././"}, - }; + private static Stream checkForDotOrDotDotPaths() { + return Stream.of( + Arguments.of("/./foo"), + Arguments.of("/../foo"), + Arguments.of("/../foo/.."), + Arguments.of("/foo/.."), + Arguments.of("/foo/."), + Arguments.of("/.."), + Arguments.of("/."), + Arguments.of("/.foo/./"), + Arguments.of("/.././") + ); } // Zip entry names to create a Zip file with for validating they are not @@ -111,7 +114,8 @@ public class HasDotDotTest { * @param path * @throws IOException */ - @Test(dataProvider = "checkForDotOrDotDotPaths") + @ParameterizedTest + @MethodSource("checkForDotOrDotDotPaths") public void hasDotOrDotDotTest(String path) throws IOException { if (DEBUG) { System.out.printf("Validating entry: %s%n", path); @@ -147,7 +151,7 @@ public class HasDotDotTest { } } Arrays.sort(EXPECTED_PATHS); - assertTrue(Arrays.equals(entries, EXPECTED_PATHS)); + assertArrayEquals(EXPECTED_PATHS, entries); } } Files.deleteIfExists(ZIPFILE); diff --git a/test/jdk/jdk/nio/zipfs/InvalidZipHeaderTests.java b/test/jdk/jdk/nio/zipfs/InvalidZipHeaderTests.java index 92acc98eb84..7a16f3026f2 100644 --- a/test/jdk/jdk/nio/zipfs/InvalidZipHeaderTests.java +++ b/test/jdk/jdk/nio/zipfs/InvalidZipHeaderTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -21,10 +21,6 @@ * questions. */ -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.FileOutputStream; import java.io.IOException; @@ -36,9 +32,17 @@ import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.stream.Collectors; +import java.util.stream.Stream; import static java.nio.file.Files.walk; -import static org.testng.Assert.*; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; /** * @test @@ -46,7 +50,7 @@ import static org.testng.Assert.*; * @summary Validate that you can iterate a ZIP file with invalid ZIP header entries * @modules jdk.zipfs * @compile InvalidZipHeaderTests.java - * @run testng InvalidZipHeaderTests + * @run junit InvalidZipHeaderTests */ public class InvalidZipHeaderTests { @@ -57,16 +61,16 @@ public class InvalidZipHeaderTests { /** * Create the JAR files used by the tests */ - @BeforeClass - public void setUp() throws Exception { + @BeforeAll + public static void setUp() throws Exception { createInvalidJarFile(); } /** * Remove JAR files used by test as part of clean-up */ - @AfterClass - public void tearDown() throws Exception { + @AfterAll + public static void tearDown() throws Exception { Files.deleteIfExists(Path.of(INVALID_JAR_FILE)); } @@ -75,17 +79,17 @@ public class InvalidZipHeaderTests { * Validate that you can walk a ZIP archive with header entries * such as "foo//" */ - @Test(dataProvider = "startPaths") + @ParameterizedTest + @MethodSource("Name") public void walkInvalidHeaderTest(String startPath, List expectedPaths) throws IOException { try (FileSystem zipfs = FileSystems.newFileSystem(Path.of(INVALID_JAR_FILE))) { List result = walk(zipfs.getPath(startPath)) .map(f -> f.toString()).collect(Collectors.toList()); - assertTrue(result.equals(expectedPaths), - String.format("Error: Expected paths not found when walking" - + "%s, starting at %s%n", INVALID_JAR_FILE, - startPath)); + assertEquals(result, expectedPaths, String.format("Error: Expected paths not found when walking" + + "%s, starting at %s%n", INVALID_JAR_FILE, + startPath)); } } @@ -94,18 +98,16 @@ public class InvalidZipHeaderTests { * Starting Path for walking the ZIP archive and the expected paths to be returned * when traversing the archive */ - @DataProvider(name = "startPaths") - public static Object[][] Name() { - return new Object[][]{ - - {"luckydog", List.of("luckydog", "luckydog/outfile.txt")}, - {"/luckydog", List.of("/luckydog", "/luckydog/outfile.txt")}, - {"./luckydog", List.of("./luckydog", "./luckydog/outfile.txt")}, - {"", List.of( "", "luckydog", "luckydog/outfile.txt")}, - {"/", List.of("/", "/luckydog", "/luckydog/outfile.txt")}, - {".", List.of(".", "./luckydog", "./luckydog/outfile.txt")}, - {"./", List.of(".", "./luckydog", "./luckydog/outfile.txt")} - }; + public static Stream Name() { + return Stream.of( + Arguments.of("luckydog", List.of("luckydog", "luckydog/outfile.txt")), + Arguments.of("/luckydog", List.of("/luckydog", "/luckydog/outfile.txt")), + Arguments.of("./luckydog", List.of("./luckydog", "./luckydog/outfile.txt")), + Arguments.of("", List.of( "", "luckydog", "luckydog/outfile.txt")), + Arguments.of("/", List.of("/", "/luckydog", "/luckydog/outfile.txt")), + Arguments.of(".", List.of(".", "./luckydog", "./luckydog/outfile.txt")), + Arguments.of("./", List.of(".", "./luckydog", "./luckydog/outfile.txt")) + ); } /** diff --git a/test/jdk/jdk/nio/zipfs/LargeCompressedEntrySizeTest.java b/test/jdk/jdk/nio/zipfs/LargeCompressedEntrySizeTest.java index 1dcfe461dda..44ec884db2d 100644 --- a/test/jdk/jdk/nio/zipfs/LargeCompressedEntrySizeTest.java +++ b/test/jdk/jdk/nio/zipfs/LargeCompressedEntrySizeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -22,9 +22,6 @@ * */ -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; import java.io.IOException; import java.io.OutputStream; @@ -36,24 +33,28 @@ import java.util.Collections; import java.util.Random; import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + /** * @test * @bug 8190753 8011146 * @summary Verify that using zip filesystem for opening an outputstream for a zip entry whose * compressed size is large, doesn't run into "Negative initial size" exception - * @run testng/manual/othervm LargeCompressedEntrySizeTest + * @run junit/manual/othervm LargeCompressedEntrySizeTest */ public class LargeCompressedEntrySizeTest { private static final String LARGE_FILE_NAME = "LargeZipEntry.txt"; private static final String ZIP_FILE_NAME = "8190753-test-compressed-size.zip"; - @BeforeMethod + @BeforeEach public void setUp() throws IOException { deleteFiles(); } - @AfterMethod + @AfterEach public void tearDown() throws IOException { deleteFiles(); } diff --git a/test/jdk/jdk/nio/zipfs/LargeEntriesTest.java b/test/jdk/jdk/nio/zipfs/LargeEntriesTest.java index 3b05f3339d7..45251c95718 100644 --- a/test/jdk/jdk/nio/zipfs/LargeEntriesTest.java +++ b/test/jdk/jdk/nio/zipfs/LargeEntriesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -22,32 +22,37 @@ * */ -import org.testng.annotations.*; - import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.*; import java.security.SecureRandom; -import java.util.Arrays; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import java.util.function.Consumer; +import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import static java.lang.Boolean.TRUE; import static java.lang.String.format; import static java.util.stream.Collectors.joining; -import static org.testng.Assert.*; -/** +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +/* * @test * @bug 8230870 * @summary Test ZIP Filesystem behavior with ~64k entries * @modules jdk.zipfs - * @run testng LargeEntriesTest + * @run junit/othervm LargeEntriesTest */ public class LargeEntriesTest { @@ -86,34 +91,41 @@ public class LargeEntriesTest { private static final SecureRandom random = new SecureRandom(); /** - * Fields used for timing runs + * Fields used for timing runs. + * Toggle on by running with "-Dtiming.enabled=true". */ private static int testNumberRunning; private static long runningTestTime; private static long startTestRunTime; private static final double NANOS_IN_SECOND = 1_000_000_000.0; + private static final boolean TIMING_ENABLED = + Boolean.getBoolean("timing.enabled"); - @BeforeTest(enabled = false) - public void beforeTest() { + @BeforeAll + public static void beforeTest() { + if (!TIMING_ENABLED) return; startTestRunTime = System.nanoTime(); } - @AfterTest(enabled = false) - public void afterTest() { + @AfterAll + public static void afterTest() { + if (!TIMING_ENABLED) return; long endTestRunTime = System.nanoTime(); long duration = endTestRunTime - startTestRunTime; System.out.printf("#### Completed test run, total running time: %.4f in seconds%n", duration / NANOS_IN_SECOND); } - @BeforeMethod(enabled = false) - public static void beforeMethod() { + @BeforeEach + public void beforeMethod() { + if (!TIMING_ENABLED) return; runningTestTime = System.nanoTime(); System.out.printf("**** Starting test number: %s%n", testNumberRunning); } - @AfterMethod(enabled = false) + @AfterEach public void afterMethod() { + if (!TIMING_ENABLED) return; long endRunningTestTime = System.nanoTime(); long duration = endRunningTestTime - runningTestTime; System.out.printf("**** Completed test number: %s, Time: %.4f%n", @@ -132,7 +144,8 @@ public class LargeEntriesTest { * @throws Exception If an error occurs during the creation, verification or * deletion of the ZIP file */ - @Test(dataProvider = "zipfsMap", enabled = true) + @ParameterizedTest + @MethodSource("zipfsMap") public void testZip(Map env, int compression) throws Exception { System.out.printf("ZIP FS Map = %s, Compression mode= %s%n ", @@ -158,7 +171,8 @@ public class LargeEntriesTest { * @throws Exception If an error occurs during the creation, verification or * deletion of the ZIP file */ - @Test(dataProvider = "zip64Map", enabled = true) + @ParameterizedTest + @MethodSource("zip64Map") public void testForceZIP64End(Map env, int compression) throws Exception { System.out.printf("ZIP FS Map = %s, Compression mode= %s%n ", @@ -183,7 +197,8 @@ public class LargeEntriesTest { * @throws Exception If an error occurs during the creation, verification or * deletion of the JAR file */ - @Test(dataProvider = "zipfsMap", enabled = true) + @ParameterizedTest + @MethodSource("zipfsMap") public void testJar(Map env, int compression) throws Exception { for (int entries = ZIP64_ENTRIES - 1; entries < ZIP64_ENTRIES + 2; entries++) { Path jar = generatePath(HERE, "test", ".jar"); @@ -271,36 +286,34 @@ public class LargeEntriesTest { } /* - * DataProvider used to validate that you can create a ZIP file with and + * MethodSource used to validate that you can create a ZIP file with and * without compression. */ - @DataProvider(name = "zipfsMap") - private Object[][] zipfsMap() { - return new Object[][]{ - {Map.of("create", "true"), ZipEntry.DEFLATED}, - {Map.of("create", "true", "noCompression", "true"), - ZipEntry.STORED}, - {Map.of("create", "true", "noCompression", "false"), - ZipEntry.DEFLATED} - }; + private static Stream zipfsMap() { + return Stream.of( + Arguments.of(Map.of("create", "true"), ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "noCompression", "true"), + ZipEntry.STORED), + Arguments.of(Map.of("create", "true", "noCompression", "false"), + ZipEntry.DEFLATED) + ); } /* - * DataProvider used to validate that you can create a ZIP file with/without + * MethodSource used to validate that you can create a ZIP file with/without * ZIP64 format extensions */ - @DataProvider(name = "zip64Map") - private Object[][] zip64Map() { - return new Object[][]{ - {Map.of("create", "true", "forceZIP64End", "true"), - ZipEntry.DEFLATED}, - {Map.of("create", "true", "noCompression", "true", - "forceZIP64End", "true"), ZipEntry.STORED}, - {Map.of("create", "true", "noCompression", "false", - "forceZIP64End", "false"), ZipEntry.DEFLATED}, - {Map.of("create", "true", "noCompression", "true", - "forceZIP64End", "false"), ZipEntry.STORED} - }; + private static Stream zip64Map() { + return Stream.of( + Arguments.of(Map.of("create", "true", "forceZIP64End", "true"), + ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "noCompression", "true", + "forceZIP64End", "true"), ZipEntry.STORED), + Arguments.of(Map.of("create", "true", "noCompression", "false", + "forceZIP64End", "false"), ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "noCompression", "true", + "forceZIP64End", "false"), ZipEntry.STORED) + ); } /** @@ -319,16 +332,16 @@ public class LargeEntriesTest { // check entries with ZIP API try (ZipFile zf = new ZipFile(zipfile.toFile())) { // check entry count - assertEquals(entries, zf.size()); + assertEquals(zf.size(), entries); // check compression method and content of each entry for (int i = start; i < entries; i++) { ZipEntry ze = zf.getEntry("Entry-" + i); assertNotNull(ze); - assertEquals(method, ze.getMethod()); + assertEquals(ze.getMethod(), method); try (InputStream is = zf.getInputStream(ze)) { byte[] bytes = is.readAllBytes(); - assertTrue(Arrays.equals(bytes, ZIP_FILE_ENTRY)); + assertArrayEquals(ZIP_FILE_ENTRY, bytes); } } } @@ -342,13 +355,13 @@ public class LargeEntriesTest { path.getFileName() != null && path.getFileName().toString().equals("META-INF"))) .count(); - assertEquals(entries, count); + assertEquals(count, entries); // check content of each entry for (int i = start; i < entries; i++) { Path file = fs.getPath("Entry-" + i); byte[] bytes = Files.readAllBytes(file); - assertTrue(Arrays.equals(bytes, ZIP_FILE_ENTRY)); + assertArrayEquals(ZIP_FILE_ENTRY, bytes); } } @@ -359,7 +372,7 @@ public class LargeEntriesTest { boolean requireZip64 = entries >= ZIP64_ENTRIES || isZip64Forced; System.out.printf(" isZip64Forced = %s, foundZip64= %s, requireZip64= %s%n", isZip64Forced, foundZip64, requireZip64); - assertEquals(requireZip64, foundZip64); + assertEquals(foundZip64, requireZip64); } @@ -515,7 +528,7 @@ public class LargeEntriesTest { * @return This Result object */ Result assertSuccess() { - assertEquals(ec, 0, format("Expected ec 0, received: %s, output [%s]", ec, output)); + assertEquals(0, ec, format("Expected ec 0, received: %s, output [%s]", ec, output)); return this; } diff --git a/test/jdk/jdk/nio/zipfs/testng/test/ManifestOrderTest.java b/test/jdk/jdk/nio/zipfs/ManifestOrderTest.java similarity index 91% rename from test/jdk/jdk/nio/zipfs/testng/test/ManifestOrderTest.java rename to test/jdk/jdk/nio/zipfs/ManifestOrderTest.java index e3ee1bac778..7eb8f70d1b7 100644 --- a/test/jdk/jdk/nio/zipfs/testng/test/ManifestOrderTest.java +++ b/test/jdk/jdk/nio/zipfs/ManifestOrderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -21,33 +21,45 @@ * questions. * */ -package test; -import org.testng.annotations.BeforeSuite; -import org.testng.annotations.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import util.ZipFsBaseTest; import java.io.InputStream; import java.io.OutputStream; -import java.nio.file.*; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import java.util.jar.*; +import java.util.jar.Attributes; import java.util.jar.Attributes.Name; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.jar.Manifest; import java.util.spi.ToolProvider; import java.util.stream.Collectors; import java.util.zip.ZipEntry; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; -/** +/* * @test * @bug 8211917 * @summary Validate that Zip FS will always add META-INF/MANIFEST.MF to the * beginning of a Zip file allowing the Manifest be found and processed * by java.util.jar.JarInputStream. - + * @run junit ManifestOrderTest */ public class ManifestOrderTest extends ZipFsBaseTest { @@ -73,8 +85,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { /** * Create the Manifests and Map of attributes included in the Manifests */ - @BeforeSuite - public void setup() { + @BeforeAll + public static void setup() { String jdkVendor = System.getProperty("java.vendor"); String jdkVersion = System.getProperty("java.version"); String attributeKey = "Player"; @@ -109,7 +121,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void testJarWithManifestAddedFirst(final Map env, final int compression) throws Exception { @@ -137,7 +150,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void testJarWithManifestAddedLast(final Map env, final int compression) throws Exception { final Path jarPath = generatePath(HERE, "test", ".jar"); @@ -164,7 +178,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void testJarWithManifestAddedInBetween(final Map env, final int compression) throws Exception { @@ -192,7 +207,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void testJarWithNoManifest(final Map env, final int compression) throws Exception { final Path jarPath = generatePath(HERE, "test", ".jar"); @@ -218,7 +234,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void testManifestCopiedFromOSFile(final Map env, final int compression) throws Exception { final Path jarPath = generatePath(HERE, "test", ".jar"); @@ -248,7 +265,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void copyJarToJarTest(final Map env, final int compression) throws Exception { final Path jarPath = generatePath(HERE, "test", ".jar"); @@ -290,7 +308,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "compressionMethods") + @ParameterizedTest + @MethodSource("compressionMethods") public void testJarToolGeneratedJarWithManifest(final int compression) throws Exception { @@ -319,7 +338,7 @@ public class ManifestOrderTest extends ZipFsBaseTest { jarPath.getFileName().toString(), manifestFile.toAbsolutePath().toString(), "-C", tmpdir.toAbsolutePath().toString(), "."); - assertEquals(exitCode, 0, "jar tool exited with failure"); + assertEquals(0, exitCode, "jar tool exited with failure"); verify(jarPath, MANIFEST_ATTRS, compression, entries); // Add an additional entry and re-verify @@ -338,7 +357,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void createWithManifestTest(final Map env, final int compression) throws Exception { Path jarPath = generatePath(HERE, "test", ".jar"); @@ -373,7 +393,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void updateManifestTest(final Map env, final int compression) throws Exception { final Path jarPath = generatePath(HERE, "test", ".jar"); @@ -442,12 +463,12 @@ public class ManifestOrderTest extends ZipFsBaseTest { } final Entry e = expected.remove(je.getName()); assertNotNull(e, "Unexpected entry in jar "); - assertEquals(je.getMethod(), e.method, "Compression method mismatch"); - assertEquals(jis.readAllBytes(), e.bytes); + assertEquals(e.method, je.getMethod(), "Compression method mismatch"); + assertArrayEquals(e.bytes, jis.readAllBytes()); je = jis.getNextJarEntry(); } - assertEquals(expected.size(), 0, "Missing entries in jar!"); + assertEquals(0, expected.size(), "Missing entries in jar!"); } } @@ -462,14 +483,14 @@ public class ManifestOrderTest extends ZipFsBaseTest { System.out.printf("Entry Name: %s, method: %s, Expected Method: %s%n", e.name, je.getMethod(), e.method); } - assertEquals(e.method, je.getMethod(), "Compression methods mismatch"); + assertEquals(je.getMethod(), e.method, "Compression methods mismatch"); try (InputStream in = jf.getInputStream(je)) { byte[] bytes = in.readAllBytes(); if (DEBUG) { System.out.printf("bytes= %s, actual=%s%n", new String(bytes), new String(e.bytes)); } - assertTrue(Arrays.equals(bytes, e.bytes), "Entries do not match"); + assertArrayEquals(bytes, e.bytes, "Entries do not match"); } } } @@ -480,7 +501,7 @@ public class ManifestOrderTest extends ZipFsBaseTest { Path top = fs.getPath("/"); long count = Files.find(top, Integer.MAX_VALUE, (path, attrs) -> attrs.isRegularFile()).count(); - assertEquals(entries.length + (!attributes.isEmpty() ? 1 : 0), count); + assertEquals(count, entries.length + (!attributes.isEmpty() ? 1 : 0)); Path mf = fs.getPath("META-INF", "MANIFEST.MF"); Manifest m = null; @@ -497,7 +518,7 @@ public class ManifestOrderTest extends ZipFsBaseTest { System.out.printf("Entry name = %s, bytes= %s, actual=%s%n", e.name, new String(Files.readAllBytes(file)), new String(e.bytes)); } - assertEquals(Files.readAllBytes(file), e.bytes); + assertArrayEquals(e.bytes, Files.readAllBytes(file)); } } } @@ -519,7 +540,7 @@ public class ManifestOrderTest extends ZipFsBaseTest { System.out.printf("Key: %s, Value: %s%n", k, v); } assertTrue(attrs.containsKey(k)); - assertEquals(v, attrs.get(k)); + assertEquals(attrs.get(k), v); }); } else { assertNull(m, "Manifest was found!"); diff --git a/test/jdk/jdk/nio/zipfs/NewFileSystemTests.java b/test/jdk/jdk/nio/zipfs/NewFileSystemTests.java index d9da79be126..b72fc765b68 100644 --- a/test/jdk/jdk/nio/zipfs/NewFileSystemTests.java +++ b/test/jdk/jdk/nio/zipfs/NewFileSystemTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -21,11 +21,6 @@ * questions. */ -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.testng.SkipException; import java.io.IOException; import java.net.URI; @@ -36,14 +31,26 @@ import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.Iterator; import java.util.Map; +import java.util.stream.Stream; + import jdk.test.lib.Platform; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullSource; /** * @test @@ -52,7 +59,7 @@ import static org.testng.Assert.assertTrue; * @modules jdk.zipfs * @library /test/lib * @compile NewFileSystemTests.java - * @run testng NewFileSystemTests + * @run junit NewFileSystemTests */ public class NewFileSystemTests { @@ -68,8 +75,8 @@ public class NewFileSystemTests { /** * Create the JAR file used by the tests */ - @BeforeClass - public void setUp() throws Exception { + @BeforeAll + public static void setUp() throws Exception { jarFile = Utils.createJarFile("basic.jar", "README"); jarURI = new URI(ZIPFS_SCHEME, jarFile.toUri().toString(), null); @@ -79,8 +86,8 @@ public class NewFileSystemTests { /** * Remove JAR file used by test as part of clean-up */ - @AfterClass - public void tearDown() throws Exception { + @AfterAll + public static void tearDown() throws Exception { Files.deleteIfExists(jarFile); } @@ -117,7 +124,9 @@ public class NewFileSystemTests { * * @throws IOException */ - @Test(dataProvider = "classLoaders") + @ParameterizedTest + @MethodSource("classLoaders") + @NullSource public void testNewFileSystemPathClassLoader(ClassLoader cl) throws Exception { try (FileSystem zipfs = FileSystems.newFileSystem(Path.of("basic.jar"), cl)) { @@ -131,7 +140,9 @@ public class NewFileSystemTests { * * @throws IOException */ - @Test(dataProvider = "classLoaders") + @ParameterizedTest + @MethodSource("classLoaders") + @NullSource public void testNewFileSystemPathMapClassLoader(ClassLoader cl) throws Exception { try (FileSystem zipfs = FileSystems.newFileSystem(Path.of("basic.jar"), ZIPFS_OPTIONS, cl)) { @@ -158,7 +169,9 @@ public class NewFileSystemTests { * * @throws IOException */ - @Test(dataProvider = "classLoaders") + @ParameterizedTest + @MethodSource("classLoaders") + @NullSource public void testNewFileSystemURIMapClassLoader(ClassLoader cl) throws Exception { try (FileSystem zipfs = FileSystems.newFileSystem(jarURI, ZIPFS_OPTIONS, cl)) { @@ -184,17 +197,17 @@ public class NewFileSystemTests { * opened if the underlying file is missing, but even with this set, a ZIP * file system cannot be opened for conflicting or invalid access modes. */ - @DataProvider(name = "badEnvMap") - protected Object[][] badEnvMap() { - return new Object[][]{ - {Map.of(), NoSuchFileException.class}, - {Map.of("accessMode", "readOnly"), NoSuchFileException.class}, - {Map.of("accessMode", "readWrite"), NoSuchFileException.class}, - {Map.of("create", true, "accessMode", "readOnly"), IllegalArgumentException.class}, - {Map.of("create", true, "accessMode", "badValue"), IllegalArgumentException.class}, - }; + protected static Stream badEnvMap() { + return Stream.of( + Arguments.of(Map.of(), NoSuchFileException.class), + Arguments.of(Map.of("accessMode", "readOnly"), NoSuchFileException.class), + Arguments.of(Map.of("accessMode", "readWrite"), NoSuchFileException.class), + Arguments.of(Map.of("create", true, "accessMode", "readOnly"), IllegalArgumentException.class), + Arguments.of(Map.of("create", true, "accessMode", "badValue"), IllegalArgumentException.class) + ); } - @Test(dataProvider = "badEnvMap") + @ParameterizedTest + @MethodSource("badEnvMap") public void badArgumentsFailure(Map env, Class exception) throws IOException { assertThrows(exception, () -> FileSystems.newFileSystem(Path.of("no_such.zip"), env)); } @@ -209,9 +222,7 @@ public class NewFileSystemTests { Path multiReleaseJar = createMultiReleaseJar(); try (FileSystem fs = FileSystems.newFileSystem(multiReleaseJar, Map.of("accessMode", "readWrite"))) { assertFalse(fs.isReadOnly()); - assertEquals( - Files.readString(fs.getPath("file.txt"), UTF_8), - "Default version", + assertEquals("Default version", Files.readString(fs.getPath("file.txt"), UTF_8), "unexpected file content"); } } @@ -222,9 +233,7 @@ public class NewFileSystemTests { */ @Test public void readOnlyZipFileFailure() throws IOException { - if (Platform.isRoot()) { - throw new SkipException("Test skipped when executed by root user."); - } + Assumptions.assumeFalse(Platform.isRoot(), "Test skipped when executed by root user."); // Underlying file is read-only. Path readOnlyZip = Utils.createJarFile("read_only.zip", Map.of("file.txt", "Hello World")); // In theory this can fail, and we should avoid unwanted false-negatives. @@ -243,9 +252,7 @@ public class NewFileSystemTests { Path multiReleaseJar = createMultiReleaseJar(); try (FileSystem fs = FileSystems.newFileSystem(multiReleaseJar, Map.of("releaseVersion", "1"))) { assertTrue(fs.isReadOnly()); - assertEquals( - Files.readString(fs.getPath("file.txt"), UTF_8), - "First version", + assertEquals("First version", Files.readString(fs.getPath("file.txt"), UTF_8), "unexpected file content"); } } @@ -273,15 +280,13 @@ public class NewFileSystemTests { } /* - * DataProvider used to verify that a Zip file system may be returned + * MethodSource used to verify that a Zip file system may be returned * when specifying a class loader */ - @DataProvider(name = "classLoaders") - private Object[][] classLoaders() { - return new Object[][]{ - {null}, - {ClassLoader.getSystemClassLoader()} - }; + private static Stream classLoaders() { + return Stream.of( + Arguments.of(ClassLoader.getSystemClassLoader()) + ); } /** @@ -294,11 +299,11 @@ public class NewFileSystemTests { assertNotNull(fs, "Error: FileSystem was not returned"); assertTrue(fs.provider().getScheme().equalsIgnoreCase(ZIPFS_SCHEME)); assertTrue(fs.isOpen()); - assertEquals(fs.getSeparator(), "/"); + assertEquals("/", fs.getSeparator()); // one root Iterator roots = fs.getRootDirectories().iterator(); - assertTrue(roots.next().toString().equals("/")); + assertEquals("/", roots.next().toString()); assertFalse(roots.hasNext()); } } diff --git a/test/jdk/jdk/nio/zipfs/NonExistentPathTests.java b/test/jdk/jdk/nio/zipfs/NonExistentPathTests.java index 20ed552212c..70c864c44ea 100644 --- a/test/jdk/jdk/nio/zipfs/NonExistentPathTests.java +++ b/test/jdk/jdk/nio/zipfs/NonExistentPathTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -21,17 +21,15 @@ * questions. * */ -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; import java.io.IOException; import java.net.URI; -import java.nio.file.FileSystemNotFoundException; import java.nio.file.FileSystems; import java.nio.file.Path; import java.util.Map; -import static org.testng.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrows; +import org.junit.jupiter.api.Test; /** * @test @@ -39,7 +37,7 @@ import static org.testng.Assert.assertThrows; * @summary Validate the correct Exception is thrown if the Zip/JAR is not found * * @modules jdk.zipfs - * @run testng/othervm NonExistentPathTests + * @run junit/othervm NonExistentPathTests */ public class NonExistentPathTests { private static final String ZIPFS_SCHEME = "jar"; diff --git a/test/jdk/jdk/nio/zipfs/PathOps.java b/test/jdk/jdk/nio/zipfs/PathOps.java index b976894a301..a035d425a3d 100644 --- a/test/jdk/jdk/nio/zipfs/PathOps.java +++ b/test/jdk/jdk/nio/zipfs/PathOps.java @@ -21,6 +21,14 @@ * questions. */ +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.FileSystems; @@ -29,607 +37,573 @@ import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.ProviderMismatchException; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; /* - * * @test * @bug 8038500 8040059 8139956 8146754 8172921 8186142 8326487 * @summary Tests path operations for zip provider. - * * @modules jdk.zipfs - * @run main PathOps - * @run main/othervm PathOps + * @run junit PathOps + * @run junit/othervm PathOps */ - public class PathOps { - static final java.io.PrintStream out = System.out; static FileSystem fs; - private Path path; - private Exception exc; - - private PathOps(String first, String... more) { - out.println(); - try { - path = fs.getPath(first, more); - out.format("%s -> %s", first, path); - } catch (Exception x) { - exc = x; - out.format("%s -> %s", first, x); - } - out.println(); + // This test uses a static file system since some ops tested on + // Path depend on the same underlying `fs` instance + @BeforeAll + static void setup() throws IOException { + // create empty JAR file, test doesn't require any contents + Path emptyJar = Utils.createJarFile("empty.jar"); + fs = FileSystems.newFileSystem(emptyJar); } - Path path() { - return path; + @AfterAll + static void cleanup() throws IOException { + fs.close(); } - void fail() { - throw new RuntimeException("PathOps failed"); + @Test + void nullPointerTest() { + Path path = fs.getPath("foo"); + assertThrows(NullPointerException.class, () -> path.resolve((String) null)); + assertThrows(NullPointerException.class, () -> path.relativize(null)); + assertThrows(NullPointerException.class, () -> path.compareTo(null)); + assertThrows(NullPointerException.class, () -> path.startsWith((Path) null)); + assertThrows(NullPointerException.class, () -> path.endsWith((Path) null)); } - void checkPath() { - if (path == null) { - throw new InternalError("path is null"); - } + @Test + void mismatchedProvidersTest() { + Path path = fs.getPath("foo"); + Path other = Paths.get("foo"); + assertThrows(ProviderMismatchException.class, () -> path.compareTo(other)); + assertThrows(ProviderMismatchException.class, () -> path.resolve(other)); + assertThrows(ProviderMismatchException.class, () -> path.relativize(other)); + assertFalse(path.startsWith(other), "providerMismatched startsWith() returns true "); + assertFalse(path.endsWith(other), "providerMismatched endsWith() returns true "); } - void check(Object result, String expected) { - out.format("\tExpected: %s\n", expected); - out.format("\tActual: %s\n", result); + @ParameterizedTest + @MethodSource + void constructionTest(String first, String[] more, String expected) { + string(getPath(first, more), expected); + } + + static Stream constructionTest() { + return Stream.of( + Arguments.of("/", new String[]{}, "/"), + Arguments.of("/", new String[]{""}, "/"), + Arguments.of("/", new String[]{"foo"}, "/foo"), + Arguments.of("/", new String[]{"/foo"}, "/foo"), + Arguments.of("/", new String[]{"foo/"}, "/foo"), + Arguments.of("foo", new String[]{"bar", "gus"}, "foo/bar/gus"), + Arguments.of("", new String[]{}, ""), + Arguments.of("", new String[]{"/"}, "/"), + Arguments.of("", new String[]{"foo", "", "bar", "", "/gus"}, "foo/bar/gus") + ); + } + + @Test + void allComponentsTest() { + var path = getPath("/a/b/c"); + root(path, "/"); + parent(path, "/a/b"); + name(path, "c"); + } + + @ParameterizedTest + @MethodSource + void nameCountTest(String first, String root, String parent, String name, int nameCount) { + var path = getPath(first); + root(path, root); + parent(path, parent); + name(path, name); + nameCount(path, nameCount); + } + + static Stream nameCountTest() { + return Stream.of( + // root component only + Arguments.of("/", "/", null, null, 0), + // empty name + Arguments.of("", null, null, "", 1) + ); + } + + @ParameterizedTest + @MethodSource + void parentNameTest(String first, String root, String parent, String name) { + var path = getPath(first); + root(path, root); + parent(path, parent); + name(path, name); + } + + static Stream parentNameTest() { + return Stream.of( + // no root component + Arguments.of("a/b", null, "a", "b"), + // name component only + Arguments.of("foo", null, null, "foo") + ); + } + + @ParameterizedTest + @MethodSource + void startsWithTest(String first, String prefix) { + starts(getPath(first), prefix); + } + + static Stream startsWithTest() { + return Stream.of( + Arguments.of("", ""), + Arguments.of("/", "/"), + Arguments.of("foo", "foo"), + Arguments.of("/foo", "/"), + Arguments.of("/foo", "/foo"), + Arguments.of("/foo/bar", "/"), + Arguments.of("/foo/bar", "/foo"), + Arguments.of("/foo/bar", "/foo/"), + Arguments.of("/foo/bar", "/foo/bar"), + Arguments.of("foo/bar", "foo"), + Arguments.of("foo/bar", "foo/"), + Arguments.of("foo/bar", "foo/bar") + ); + } + + @ParameterizedTest + @MethodSource + void notStartsWithTest(String first, String prefix) { + notStarts(getPath(first), prefix); + } + + static Stream notStartsWithTest() { + return Stream.of( + Arguments.of("", "/"), + Arguments.of("/", "/foo"), + Arguments.of("foo", "f"), + Arguments.of("/foo", "/f"), + Arguments.of("/foo", ""), + Arguments.of("/foo/bar", "/f"), + Arguments.of("/foo/bar", "foo"), + Arguments.of("/foo/bar", "foo/bar"), + Arguments.of("/foo/bar", ""), + Arguments.of("foo/bar", "f"), + Arguments.of("foo/bar", "/foo"), + Arguments.of("foo/bar", "/foo/bar") + ); + } + + @ParameterizedTest + @MethodSource + void endsWithTest(String first, String suffix) { + ends(getPath(first), suffix); + } + + static Stream endsWithTest() { + return Stream.of( + Arguments.of("", ""), + Arguments.of("/", "/"), + Arguments.of("/foo", "foo"), + Arguments.of("/foo","/foo"), + Arguments.of("/foo/bar", "bar"), + Arguments.of("/foo/bar", "foo/bar"), + Arguments.of("/foo/bar", "foo/bar/"), + Arguments.of("/foo/bar", "/foo/bar"), + Arguments.of("/foo/bar/", "bar"), + Arguments.of("/foo/bar/", "foo/bar"), + Arguments.of("/foo/bar/", "foo/bar/"), + Arguments.of("/foo/bar/", "/foo/bar"), + Arguments.of("foo", "foo"), + Arguments.of("foo/bar", "bar"), + Arguments.of("foo/bar", "bar/"), + Arguments.of("foo/bar", "foo/bar/"), + Arguments.of("foo/bar", "foo/bar") + ); + } + + @ParameterizedTest + @MethodSource + void notEndsWithTest(String first, String suffix) { + notEnds(getPath(first), suffix); + } + + static Stream notEndsWithTest() { + return Stream.of( + Arguments.of("", "/"), + Arguments.of("/", "foo"), + Arguments.of("/", "/foo"), + Arguments.of("/foo", "/"), + Arguments.of("/foo/bar", "/bar"), + Arguments.of("/foo/bar/", "/bar") + ); + } + + @ParameterizedTest + @MethodSource + void elementTest(int index, String expected) { + element(getPath("a/b/c"), index, expected); + } + + static Stream elementTest() { + return Stream.of( + Arguments.of(0, "a"), + Arguments.of(1, "b"), + Arguments.of(2, "c") + ); + } + + @ParameterizedTest + @ValueSource(strings = {"/", "/tmp"} ) + void isAbsoluteTest(String first) { + absolute(getPath(first)); + } + + @ParameterizedTest + @ValueSource(strings = {"tmp", ""} ) + void notAbsoluteTest(String first) { + notAbsolute(getPath(first)); + } + + @ParameterizedTest + @MethodSource + void resolveTest(String first, String other, String expected) { + resolve(getPath(first), other, expected); + } + + static Stream resolveTest() { + return Stream.of( + Arguments.of("/tmp", "foo", "/tmp/foo"), + Arguments.of("/tmp", "/foo", "/foo"), + Arguments.of("/tmp", "", "/tmp"), + Arguments.of("tmp", "foo", "tmp/foo"), + Arguments.of("tmp", "/foo", "/foo"), + Arguments.of("tmp", "", "tmp"), + Arguments.of("", "", ""), + Arguments.of("", "foo", "foo"), + Arguments.of("", "/foo", "/foo"), + Arguments.of("/", "", "/"), + Arguments.of("/", "foo", "/foo"), + Arguments.of("/", "/foo", "/foo"), + Arguments.of("/", "/foo/", "/foo") + ); + } + + @ParameterizedTest + @MethodSource + void resolvePathTest(String first, String other, String expected) { + resolvePath(getPath(first), other, expected); + } + + static Stream resolvePathTest() { + return Stream.of( + Arguments.of("/tmp", "foo", "/tmp/foo"), + Arguments.of("/tmp", "/foo", "/foo"), + Arguments.of("/tmp", "", "/tmp"), + Arguments.of("tmp", "foo", "tmp/foo"), + Arguments.of("tmp", "/foo", "/foo"), + Arguments.of("tmp", "", "tmp"), + Arguments.of("", "", ""), + Arguments.of("", "foo", "foo"), + Arguments.of("", "/foo", "/foo"), + Arguments.of("/", "", "/"), + Arguments.of("/", "foo", "/foo"), + Arguments.of("/", "/foo", "/foo"), + Arguments.of("/", "/foo/", "/foo") + ); + } + + @ParameterizedTest + @MethodSource + void resolveSiblingTest(String first, String other, String expected) { + resolveSibling(getPath(first), other, expected); + } + + static Stream resolveSiblingTest() { + return Stream.of( + Arguments.of("foo", "bar", "bar"), + Arguments.of("foo", "/bar", "/bar"), + Arguments.of("foo", "", ""), + Arguments.of("foo/bar", "gus", "foo/gus"), + Arguments.of("foo/bar", "/gus", "/gus"), + Arguments.of("foo/bar", "", "foo"), + Arguments.of("/foo", "gus", "/gus"), + Arguments.of("/foo", "/gus", "/gus"), + Arguments.of("/foo", "", "/"), + Arguments.of("/foo/bar", "gus", "/foo/gus"), + Arguments.of("/foo/bar", "/gus", "/gus"), + Arguments.of("/foo/bar", "", "/foo") + ); + } + + @Test + void resolveSiblingAndResolveTest() { + var path = getPath(""); + resolveSibling(path, "foo", "foo"); + resolveSibling(path, "/foo", "/foo"); + resolve(path, "", ""); + } + + @ParameterizedTest + @MethodSource + void relativizeTest(String first, String other, String expected) { + relativize(getPath(first), other, expected); + } + + static Stream relativizeTest() { + return Stream.of( + Arguments.of("/a/b/c", "/a/b/c", ""), + Arguments.of("/a/b/c", "/a/b/c/d/e", "d/e"), + Arguments.of("/a/b/c", "/a/x", "../../x"), + Arguments.of("/a/b/c", "/x", "../../../x"), + Arguments.of("a/b/c", "a/b/c/d", "d"), + Arguments.of("a/b/c", "a/x", "../../x"), + Arguments.of("a/b/c", "x", "../../../x"), + Arguments.of("a/b/c", "", "../../.."), + Arguments.of("", "a", "a"), + Arguments.of("", "a/b/c", "a/b/c"), + Arguments.of("", "", ""), + Arguments.of("/", "/a", "a"), + Arguments.of("/", "/a/c", "a/c"), + // 8146754 + Arguments.of("/tmp/path", "/tmp/path/a.txt", "a.txt"), + Arguments.of("/tmp/path/", "/tmp/path/a.txt", "a.txt") + ); + } + + @ParameterizedTest + @MethodSource + void normalizeTest(String first, String expected) { + normalize(getPath(first), expected); + } + + static Stream normalizeTest() { + return Stream.of( + Arguments.of("/", "/"), + Arguments.of("foo", "foo"), + Arguments.of("/foo", "/foo"), + Arguments.of(".", ""), + Arguments.of("..", ".."), + Arguments.of("/..", "/"), + Arguments.of("/../..", "/"), + Arguments.of("foo/.", "foo"), + Arguments.of("./foo", "foo"), + Arguments.of("foo/..", ""), + Arguments.of("../foo", "../foo"), + Arguments.of("../../foo", "../../foo"), + Arguments.of("foo/bar/..", "foo"), + Arguments.of("foo/bar/gus/../..", "foo"), + Arguments.of("/foo/bar/gus/../..", "/foo"), + Arguments.of("/./.", "/"), + Arguments.of("/.", "/"), + Arguments.of("/./abc", "/abc") + ); + } + + @ParameterizedTest + @MethodSource + void invalidTest(String first) { + assertThrows(InvalidPathException.class, () -> getPath(first)); + } + + static Stream invalidTest() { + return Stream.of( + "foo\u0000bar", + "\u0000foo", + "bar\u0000", + "//foo\u0000bar", + "//\u0000foo", + "//bar\u0000" + ); + } + + @Test + void normalizationTest() { + var path = getPath("//foo//bar"); + string(path, "/foo/bar"); + root(path, "/"); + parent(path, "/foo"); + name(path, "bar"); + } + + @Test + void isSameFileTest() { + isSameFile(getPath("/fileDoesNotExist"), "/fileDoesNotExist"); + } + + @Test + void getNameCountTest() { + // 8139956 + System.out.println("check getNameCount"); + int nc = fs.getPath("/").relativize(fs.getPath("/")).getNameCount(); + assertEquals(1, nc, "getNameCount of empty path failed"); + } + + // Utilities for testing + + static void checkPath(Path path) { + assertNotNull(path, "path is null"); + } + + static void check(Object result, String expected) { if (result == null) { if (expected == null) return; } else { // compare string representations - if (expected != null && result.toString().equals(expected.toString())) + if (expected != null && result.toString().equals(expected)) return; } fail(); } - void check(Object result, boolean expected) { + static void check(Object result, boolean expected) { check(result, Boolean.toString(expected)); } - void check(Object result, int expected) { + static void check(Object result, int expected) { check(result, Integer.toString(expected)); } - PathOps root(String expected) { - out.println("check root"); - checkPath(); + static void root(Path path, String expected) { + System.out.println("check root"); + checkPath(path); check(path.getRoot(), expected); - return this; } - PathOps parent(String expected) { - out.println("check parent"); - checkPath(); + static void parent(Path path, String expected) { + System.out.println("check parent"); + checkPath(path); check(path.getParent(), expected); - return this; } - PathOps name(String expected) { - out.println("check name"); - checkPath(); + static void name(Path path, String expected) { + System.out.println("check name"); + checkPath(path); check(path.getFileName(), expected); - return this; } - PathOps nameCount(int expected) { - out.println("check nameCount"); - checkPath(); + static void nameCount(Path path, int expected) { + System.out.println("check nameCount"); + checkPath(path); check(path.getNameCount(), expected); - return this; } - PathOps element(int index, String expected) { - out.format("check element %d\n", index); - checkPath(); + static void element(Path path, int index, String expected) { + System.out.format("check element %d\n", index); + checkPath(path); check(path.getName(index), expected); - return this; } - PathOps subpath(int startIndex, int endIndex, String expected) { - out.format("test subpath(%d,%d)\n", startIndex, endIndex); - checkPath(); + static void subpath(Path path, int startIndex, int endIndex, String expected) { + System.out.format("test subpath(%d,%d)\n", startIndex, endIndex); + checkPath(path); check(path.subpath(startIndex, endIndex), expected); - return this; } - PathOps starts(String prefix) { - out.format("test startsWith with %s\n", prefix); - checkPath(); + static void starts(Path path, String prefix) { + System.out.format("test startsWith with %s\n", prefix); + checkPath(path); Path s = fs.getPath(prefix); check(path.startsWith(s), true); - return this; } - PathOps notStarts(String prefix) { - out.format("test not startsWith with %s\n", prefix); - checkPath(); + static void notStarts(Path path, String prefix) { + System.out.format("test not startsWith with %s\n", prefix); + checkPath(path); Path s = fs.getPath(prefix); check(path.startsWith(s), false); - return this; } - PathOps ends(String suffix) { - out.format("test endsWith %s\n", suffix); - checkPath(); + static void ends(Path path, String suffix) { + System.out.format("test endsWith %s\n", suffix); + checkPath(path); Path s = fs.getPath(suffix); check(path.endsWith(s), true); - return this; } - PathOps notEnds(String suffix) { - out.format("test not endsWith %s\n", suffix); - checkPath(); + static void notEnds(Path path, String suffix) { + System.out.format("test not endsWith %s\n", suffix); + checkPath(path); Path s = fs.getPath(suffix); check(path.endsWith(s), false); - return this; } - PathOps absolute() { - out.println("check path is absolute"); - checkPath(); + static void absolute(Path path) { + System.out.println("check path is absolute"); + checkPath(path); check(path.isAbsolute(), true); - return this; } - PathOps notAbsolute() { - out.println("check path is not absolute"); - checkPath(); + static void notAbsolute(Path path) { + System.out.println("check path is not absolute"); + checkPath(path); check(path.isAbsolute(), false); - return this; } - PathOps resolve(String other, String expected) { - out.format("test resolve %s\n", other); - checkPath(); + static void resolve(Path path, String other, String expected) { + System.out.format("test resolve %s\n", other); + checkPath(path); check(path.resolve(other), expected); - return this; } - PathOps resolvePath(String other, String expected) { - out.format("test resolve %s\n", other); - checkPath(); + static void resolvePath(Path path, String other, String expected) { + System.out.format("test resolve %s\n", other); + checkPath(path); check(path.resolve(fs.getPath(other)), expected); - return this; } - PathOps resolveSibling(String other, String expected) { - out.format("test resolveSibling %s\n", other); - checkPath(); + static void resolveSibling(Path path, String other, String expected) { + System.out.format("test resolveSibling %s\n", other); + checkPath(path); check(path.resolveSibling(other), expected); - return this; + } - PathOps relativize(String other, String expected) { - out.format("test relativize %s\n", other); - checkPath(); + static void relativize(Path path, String other, String expected) { + System.out.format("test relativize %s\n", other); + checkPath(path); Path that = fs.getPath(other); check(path.relativize(that), expected); - return this; + } - PathOps normalize(String expected) { - out.println("check normalized path"); - checkPath(); + static void normalize(Path path, String expected) { + System.out.println("check normalized path"); + checkPath(path); check(path.normalize(), expected); - return this; + } - PathOps string(String expected) { - out.println("check string representation"); - checkPath(); + static void string(Path path, String expected) { + System.out.println("check string representation"); + checkPath(path); check(path, expected); - return this; } - PathOps isSameFile(String target) { + static void isSameFile(Path path, String target) { try { - out.println("check two paths are same"); - checkPath(); - check(Files.isSameFile(path, test(target).path()), true); + System.out.println("check two paths are same"); + checkPath(path); + check(Files.isSameFile(path, fs.getPath(target)), true); } catch (IOException ioe) { fail(); } - return this; } - PathOps invalid() { - if (!(exc instanceof InvalidPathException)) { - out.println("InvalidPathException not thrown as expected"); - fail(); - } - return this; + static Path getPath(String s) { + return fs.getPath(s); } - static PathOps test(String s) { - return new PathOps(s); - } - - static PathOps test(String first, String... more) { - return new PathOps(first, more); - } - - // -- PathOpss -- - - static void header(String s) { - out.println(); - out.println(); - out.println("-- " + s + " --"); - } - - static void doPathOpTests() { - header("Path operations"); - - // construction - test("/") - .string("/"); - test("/", "") - .string("/"); - test("/", "foo") - .string("/foo"); - test("/", "/foo") - .string("/foo"); - test("/", "foo/") - .string("/foo"); - test("foo", "bar", "gus") - .string("foo/bar/gus"); - test("") - .string(""); - test("", "/") - .string("/"); - test("", "foo", "", "bar", "", "/gus") - .string("foo/bar/gus"); - - // all components - test("/a/b/c") - .root("/") - .parent("/a/b") - .name("c"); - - // root component only - test("/") - .root("/") - .parent(null) - .name(null) - .nameCount(0); - - // empty name - test("") - .root(null) - .parent(null) - .name("") - .nameCount(1); - - // no root component - test("a/b") - .root(null) - .parent("a") - .name("b"); - - // name component only - test("foo") - .root(null) - .parent(null) - .name("foo"); - - // startsWith - test("") - .starts("") - .notStarts("/"); - test("/") - .starts("/") - .notStarts("/foo"); - test("/foo") - .starts("/") - .starts("/foo") - .notStarts("/f") - .notStarts(""); - test("/foo/bar") - .starts("/") - .starts("/foo") - .starts("/foo/") - .starts("/foo/bar") - .notStarts("/f") - .notStarts("foo") - .notStarts("foo/bar") - .notStarts(""); - test("foo") - .starts("foo") - .notStarts("f"); - test("foo/bar") - .starts("foo") - .starts("foo/") - .starts("foo/bar") - .notStarts("f") - .notStarts("/foo") - .notStarts("/foo/bar"); - - // endsWith - test("") - .ends("") - .notEnds("/"); - test("/") - .ends("/") - .notEnds("foo") - .notEnds("/foo"); - test("/foo") - .ends("foo") - .ends("/foo") - .notEnds("/"); - test("/foo/bar") - .ends("bar") - .ends("foo/bar") - .ends("foo/bar/") - .ends("/foo/bar") - .notEnds("/bar"); - test("/foo/bar/") - .ends("bar") - .ends("foo/bar") - .ends("foo/bar/") - .ends("/foo/bar") - .notEnds("/bar"); - test("foo") - .ends("foo"); - test("foo/bar") - .ends("bar") - .ends("bar/") - .ends("foo/bar/") - .ends("foo/bar"); - - // elements - test("a/b/c") - .element(0,"a") - .element(1,"b") - .element(2,"c"); - - // isAbsolute - test("/") - .absolute(); - test("/tmp") - .absolute(); - test("tmp") - .notAbsolute(); - test("") - .notAbsolute(); - - // resolve - test("/tmp") - .resolve("foo", "/tmp/foo") - .resolve("/foo", "/foo") - .resolve("", "/tmp"); - test("tmp") - .resolve("foo", "tmp/foo") - .resolve("/foo", "/foo") - .resolve("", "tmp"); - test("") - .resolve("", "") - .resolve("foo", "foo") - .resolve("/foo", "/foo"); - test("/") - .resolve("", "/") - .resolve("foo", "/foo") - .resolve("/foo", "/foo") - .resolve("/foo/", "/foo"); - - // resolve(Path) - test("/tmp") - .resolvePath("foo", "/tmp/foo") - .resolvePath("/foo", "/foo") - .resolvePath("", "/tmp"); - test("tmp") - .resolvePath("foo", "tmp/foo") - .resolvePath("/foo", "/foo") - .resolvePath("", "tmp"); - test("") - .resolvePath("", "") - .resolvePath("foo", "foo") - .resolvePath("/foo", "/foo"); - test("/") - .resolvePath("", "/") - .resolvePath("foo", "/foo") - .resolvePath("/foo", "/foo") - .resolvePath("/foo/", "/foo"); - - // resolveSibling - test("foo") - .resolveSibling("bar", "bar") - .resolveSibling("/bar", "/bar") - .resolveSibling("", ""); - test("foo/bar") - .resolveSibling("gus", "foo/gus") - .resolveSibling("/gus", "/gus") - .resolveSibling("", "foo"); - test("/foo") - .resolveSibling("gus", "/gus") - .resolveSibling("/gus", "/gus") - .resolveSibling("", "/"); - test("/foo/bar") - .resolveSibling("gus", "/foo/gus") - .resolveSibling("/gus", "/gus") - .resolveSibling("", "/foo"); - test("") - .resolveSibling("foo", "foo") - .resolveSibling("/foo", "/foo") - .resolve("", ""); - - // relativize - test("/a/b/c") - .relativize("/a/b/c", "") - .relativize("/a/b/c/d/e", "d/e") - .relativize("/a/x", "../../x") - .relativize("/x", "../../../x"); - test("a/b/c") - .relativize("a/b/c/d", "d") - .relativize("a/x", "../../x") - .relativize("x", "../../../x") - .relativize("", "../../.."); - test("") - .relativize("a", "a") - .relativize("a/b/c", "a/b/c") - .relativize("", ""); - test("/") - .relativize("/a", "a") - .relativize("/a/c", "a/c"); - // 8146754 - test("/tmp/path") - .relativize("/tmp/path/a.txt", "a.txt"); - test("/tmp/path/") - .relativize("/tmp/path/a.txt", "a.txt"); - - // normalize - test("/") - .normalize("/"); - test("foo") - .normalize("foo"); - test("/foo") - .normalize("/foo"); - test(".") - .normalize(""); - test("..") - .normalize(".."); - test("/..") - .normalize("/"); - test("/../..") - .normalize("/"); - test("foo/.") - .normalize("foo"); - test("./foo") - .normalize("foo"); - test("foo/..") - .normalize(""); - test("../foo") - .normalize("../foo"); - test("../../foo") - .normalize("../../foo"); - test("foo/bar/..") - .normalize("foo"); - test("foo/bar/gus/../..") - .normalize("foo"); - test("/foo/bar/gus/../..") - .normalize("/foo"); - test("/./.") - .normalize("/"); - test("/.") - .normalize("/"); - test("/./abc") - .normalize("/abc"); - // invalid - test("foo\u0000bar") - .invalid(); - test("\u0000foo") - .invalid(); - test("bar\u0000") - .invalid(); - test("//foo\u0000bar") - .invalid(); - test("//\u0000foo") - .invalid(); - test("//bar\u0000") - .invalid(); - - // normalization - test("//foo//bar") - .string("/foo/bar") - .root("/") - .parent("/foo") - .name("bar"); - - // isSameFile - test("/fileDoesNotExist") - .isSameFile("/fileDoesNotExist"); - - // 8139956 - out.println("check getNameCount"); - int nc = fs.getPath("/").relativize(fs.getPath("/")).getNameCount(); - if (nc != 1) { - out.format("\tExpected: 1\n"); - out.format("\tActual: %d\n", nc); - throw new RuntimeException("getNameCount of empty path failed"); - } - } - - static void npes() { - header("NullPointerException"); - - Path path = fs.getPath("foo"); - - try { - path.resolve((String)null); - throw new RuntimeException("NullPointerException not thrown"); - } catch (NullPointerException npe) { - } - - try { - path.relativize(null); - throw new RuntimeException("NullPointerException not thrown"); - } catch (NullPointerException npe) { - } - - try { - path.compareTo(null); - throw new RuntimeException("NullPointerException not thrown"); - } catch (NullPointerException npe) { - } - - try { - path.startsWith((Path)null); - throw new RuntimeException("NullPointerException not thrown"); - } catch (NullPointerException npe) { - } - - try { - path.endsWith((Path)null); - throw new RuntimeException("NullPointerException not thrown"); - } catch (NullPointerException npe) { - } - - } - - static void mismatchedProviders() { - header("ProviderMismatchException"); - Path path = fs.getPath("foo"); - Path other = Paths.get("foo"); - try { - path.compareTo(other); - throw new RuntimeException("ProviderMismatchException not thrown"); - } catch (ProviderMismatchException pme) {} - - try { - path.resolve(other); - throw new RuntimeException("ProviderMismatchException not thrown"); - } catch (ProviderMismatchException pme) {} - - try { - path.relativize(other); - throw new RuntimeException("ProviderMismatchException not thrown"); - } catch (ProviderMismatchException pme) {} - - try { - if (path.startsWith(other)) - throw new RuntimeException("providerMismatched startsWith() returns true "); - if (path.endsWith(other)) - throw new RuntimeException("providerMismatched endsWith() returns true "); - } catch (ProviderMismatchException pme) { - throw new RuntimeException("ProviderMismatchException is thrown for starts/endsWith()"); - } - } - - public static void main(String[] args) throws IOException { - // create empty JAR file, test doesn't require any contents - Path emptyJar = Utils.createJarFile("empty.jar"); - - fs = FileSystems.newFileSystem(emptyJar); - try { - npes(); - mismatchedProviders(); - doPathOpTests(); - } finally { - fs.close(); - } + static Path getPath(String first, String... more) { + return fs.getPath(first, more); } } diff --git a/test/jdk/jdk/nio/zipfs/testng/test/PosixAttributeViewTest.java b/test/jdk/jdk/nio/zipfs/PosixAttributeViewTest.java similarity index 77% rename from test/jdk/jdk/nio/zipfs/testng/test/PosixAttributeViewTest.java rename to test/jdk/jdk/nio/zipfs/PosixAttributeViewTest.java index 7734ea733c9..ff20741135d 100644 --- a/test/jdk/jdk/nio/zipfs/testng/test/PosixAttributeViewTest.java +++ b/test/jdk/jdk/nio/zipfs/PosixAttributeViewTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -21,12 +21,12 @@ * questions. * */ -package test; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import util.ZipFsBaseTest; import java.io.IOException; @@ -36,13 +36,15 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.PosixFileAttributeView; import java.util.Map; +import java.util.stream.Stream; import java.util.zip.ZipEntry; -/** +/* * @test * @bug 8273935 * @summary Validate that Files.getFileAttributeView will not throw an * Exception when the attribute view PosixFileAttributeView is not available + * @run junit PosixAttributeViewTest */ public class PosixAttributeViewTest extends ZipFsBaseTest { public static final String ZIP_ENTRY = "Entry-0"; @@ -52,8 +54,8 @@ public class PosixAttributeViewTest extends ZipFsBaseTest { * Create initial Zip File * @throws IOException if an error occurs */ - @BeforeTest - public void setup() throws IOException { + @BeforeAll + public static void setup() throws IOException { Files.deleteIfExists(ZIP_FILE); Entry entry = Entry.of(ZIP_ENTRY, ZipEntry.DEFLATED, "Tennis Anyone"); @@ -64,22 +66,21 @@ public class PosixAttributeViewTest extends ZipFsBaseTest { * Remove Zip File used by Test * @throws IOException if an error occurs */ - @AfterTest - public void cleanup() throws IOException { + @AfterAll + public static void cleanup() throws IOException { Files.deleteIfExists(ZIP_FILE); } /** - * DataProvider used to specify the Map indicating whether Posix + * MethodSource used to specify the Map indicating whether Posix * file attributes have been enabled * @return map of the Zip FS properties to configure */ - @DataProvider - protected Object[][] zipfsMap() { - return new Object[][]{ - {Map.of()}, - {Map.of("enablePosixFileAttributes", "true")} - }; + protected static Stream zipfsMap() { + return Stream.of( + Arguments.of(Map.of()), + Arguments.of(Map.of("enablePosixFileAttributes", "true")) + ); } /** @@ -89,7 +90,8 @@ public class PosixAttributeViewTest extends ZipFsBaseTest { * @param env map of the Zip FS properties to configure * @throws Exception if an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void testPosixAttributeView(Map env) throws Exception { try (FileSystem fs = FileSystems.newFileSystem(ZIP_FILE, env)) { Path entry = fs.getPath(ZIP_ENTRY); diff --git a/test/jdk/jdk/nio/zipfs/PropertyPermissionTests.java b/test/jdk/jdk/nio/zipfs/PropertyPermissionTests.java index de8992bce74..5f31d87f81c 100644 --- a/test/jdk/jdk/nio/zipfs/PropertyPermissionTests.java +++ b/test/jdk/jdk/nio/zipfs/PropertyPermissionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -21,9 +21,6 @@ * questions. */ -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; import java.io.IOException; import java.nio.file.FileSystem; @@ -33,13 +30,18 @@ import java.nio.file.Paths; import java.nio.file.spi.FileSystemProvider; import java.util.Map; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + + /** * @test * @bug 8210469 * @summary Verify ZIP FileSystem works with a Security Manager * @modules jdk.zipfs * @compile PropertyPermissionTests.java - * @run testng/othervm PropertyPermissionTests + * @run junit/othervm PropertyPermissionTests */ public class PropertyPermissionTests { @@ -55,8 +57,8 @@ public class PropertyPermissionTests { /** * Create the JAR files used by the tests */ - @BeforeClass - public void setUp() throws Exception { + @BeforeAll + public static void setUp() throws Exception { jarFile = Utils.createJarFile("basic.jar", "META-INF/services/java.nio.file.spi.FileSystemProvider"); } @@ -64,8 +66,8 @@ public class PropertyPermissionTests { /** * Remove JAR files used by test as part of clean-up */ - @AfterClass - public void tearDown() throws Exception { + @AfterAll + public static void tearDown() throws Exception { Files.deleteIfExists(jarFile); } diff --git a/test/jdk/jdk/nio/zipfs/ReleaseDeflater.java b/test/jdk/jdk/nio/zipfs/ReleaseDeflater.java index 82369b4fe39..95446da9267 100644 --- a/test/jdk/jdk/nio/zipfs/ReleaseDeflater.java +++ b/test/jdk/jdk/nio/zipfs/ReleaseDeflater.java @@ -1,5 +1,6 @@ /* * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * Copyright (c) 2026, 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 @@ -25,12 +26,15 @@ /* * @test * @bug 8234011 - * @summary Check that jdk.nio.zipfs.ZipFileSystem doesn't cache more than ZipFileSystem.MAX_FLATER Inflater/Deflater objects - * @run main ReleaseDeflater + * @summary Check that jdk.nio.zipfs.ZipFileSystem doesn't cache more than + * ZipFileSystem.MAX_FLATER Inflater/Deflater objects. * @modules jdk.zipfs/jdk.nio.zipfs:+open - * @author Volker Simonis + * @run junit ReleaseDeflater */ +import org.junit.jupiter.api.Test; + +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; @@ -44,8 +48,12 @@ import java.util.List; import java.util.Map; import java.util.ArrayList; +import static org.junit.jupiter.api.Assertions.assertFalse; + public class ReleaseDeflater { - public static void main(String[] args) throws Throwable { + + @Test + void cacheTest() throws IOException, IllegalAccessException { Path zipFile = Paths.get("ReleaseDeflaterTest.zip"); try (FileSystem fs = FileSystems.newFileSystem(zipFile, Map.of("create", true))) { FileSystemProvider zprov = fs.provider(); @@ -75,15 +83,11 @@ public class ReleaseDeflater { Field inflaters = fs.getClass().getDeclaredField("inflaters"); inflaters.setAccessible(true); int inflater_count = ((List) inflaters.get(fs)).size(); - if (inflater_count > MAX_FLATERS) { - throw new Exception("Too many inflaters " + inflater_count); - } + assertFalse(inflater_count > MAX_FLATERS, "Too many inflaters " + inflater_count); Field deflaters = fs.getClass().getDeclaredField("deflaters"); deflaters.setAccessible(true); int deflater_count = ((List) deflaters.get(fs)).size(); - if (deflater_count > MAX_FLATERS) { - throw new Exception("Too many deflaters " + deflater_count); - } + assertFalse(deflater_count > MAX_FLATERS, "Too many deflaters " + deflater_count); } catch (NoSuchFieldException nsfe) { // Probably the implementation has changed, so there's not much we can do... throw new RuntimeException("Implementation of jdk.nio.zipfs.ZipFileSystem changed - disable or fix the test"); @@ -91,6 +95,5 @@ public class ReleaseDeflater { } finally { Files.deleteIfExists(zipFile); } - } } diff --git a/test/jdk/jdk/nio/zipfs/UpdateEntryTest.java b/test/jdk/jdk/nio/zipfs/UpdateEntryTest.java index 6611efced82..8a65f08afc2 100644 --- a/test/jdk/jdk/nio/zipfs/UpdateEntryTest.java +++ b/test/jdk/jdk/nio/zipfs/UpdateEntryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -22,7 +22,6 @@ * */ -import org.testng.annotations.Test; import java.io.IOException; import java.io.InputStream; @@ -32,7 +31,6 @@ import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; import java.util.Map; import java.util.spi.ToolProvider; import java.util.zip.CRC32; @@ -40,16 +38,16 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /** * @test * @bug 8229887 * @summary Validate ZIP FileSystem can replace existing STORED and DEFLATED entries * @modules jdk.zipfs - * @run testng UpdateEntryTest + * @run junit UpdateEntryTest */ -@Test public class UpdateEntryTest { private static final Path HERE = Path.of("."); @@ -109,6 +107,7 @@ public class UpdateEntryTest { * Validate that you can replace an existing entry in a JAR file that * was added with the STORED(no-compression) option */ + @Test public void testReplaceStoredEntry() throws IOException { String jarFileName = "updateStoredEntry.jar"; String storedFileName = "storedFile.txt"; @@ -132,6 +131,7 @@ public class UpdateEntryTest { /** * Test updating an entry that is STORED (not compressed) */ + @Test public void test1() throws IOException { Entry e1 = Entry.of("foo", ZipEntry.STORED, "hello"); Entry e2 = Entry.of("bar", ZipEntry.STORED, "world"); @@ -141,6 +141,7 @@ public class UpdateEntryTest { /** * Test updating an entry that is DEFLATED (compressed) */ + @Test public void test2() throws IOException { Entry e1 = Entry.of("foo", ZipEntry.DEFLATED, "hello"); Entry e2 = Entry.of("bar", ZipEntry.STORED, "world"); @@ -183,16 +184,16 @@ public class UpdateEntryTest { // check entries with zip API try (ZipFile zf = new ZipFile(zipfile.toFile())) { // check entry count - assertTrue(zf.size() == entries.length); + assertEquals(zf.size(), entries.length); // check compression method and content of each entry for (Entry e : entries) { ZipEntry ze = zf.getEntry(e.name); - assertTrue(ze != null); - assertTrue(ze.getMethod() == e.method); + assertNotNull(ze); + assertEquals(ze.getMethod(), e.method); try (InputStream in = zf.getInputStream(ze)) { byte[] bytes = in.readAllBytes(); - assertTrue(Arrays.equals(bytes, e.bytes)); + assertArrayEquals(bytes, e.bytes); } } } @@ -203,13 +204,13 @@ public class UpdateEntryTest { Path top = fs.getPath("/"); long count = Files.find(top, Integer.MAX_VALUE, (path, attrs) -> attrs.isRegularFile()).count(); - assertTrue(count == entries.length); + assertEquals(count, entries.length); // check content of each entry for (Entry e : entries) { Path file = fs.getPath(e.name); byte[] bytes = Files.readAllBytes(file); - assertTrue(Arrays.equals(bytes, e.bytes)); + assertArrayEquals(bytes, e.bytes); } } } diff --git a/test/jdk/jdk/nio/zipfs/ZFSTests.java b/test/jdk/jdk/nio/zipfs/ZFSTests.java index f00ff58f877..b4e625dfe38 100644 --- a/test/jdk/jdk/nio/zipfs/ZFSTests.java +++ b/test/jdk/jdk/nio/zipfs/ZFSTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, 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 @@ -24,18 +24,20 @@ /* @test * @bug 7156873 8040059 8028480 8034773 8153248 8061777 8197398 8210394 * @summary ZipFileSystem regression tests - * * @modules jdk.zipfs - * @run main ZFSTests - * @run main/othervm ZFSTests + * @run junit ZFSTests + * @run junit/othervm ZFSTests */ +import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.OutputStream; import java.net.URI; import java.nio.ByteBuffer; import java.nio.channels.*; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.spi.*; @@ -45,16 +47,16 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + public class ZFSTests { - public static void main(String[] args) throws Throwable { - test8197398(); - test7156873(); - test8061777(); - tests(); - } + static final Charset ASCII = StandardCharsets.US_ASCII; - static void test8197398() throws Throwable { + @Test + void test8197398() throws Throwable { // root entry "/" Path path = Paths.get("rootdir.zip"); @@ -72,33 +74,29 @@ public class ZFSTests { } AtomicInteger cnt = new AtomicInteger(); int max = 3; - try (FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + try (FileSystem fs = FileSystems.newFileSystem(uri, Map.of())) { Files.walkFileTree(fs.getRootDirectories().iterator().next(), new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if (cnt.incrementAndGet() > max) - throw new RuntimeException("visited too many files/dirs"); + assertFalse(cnt.incrementAndGet() > max, "visited too many files/dirs"); files.remove(file.toString()); - if (!Arrays.equals(Files.readAllBytes(file), file.toString().getBytes())) - throw new RuntimeException("visited files has wrong content: " + file); + assertArrayEquals(file.toString().getBytes(), Files.readAllBytes(file), + "visited files has wrong content: " + file); return FileVisitResult.CONTINUE; } @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) - throws IOException { - if (cnt.incrementAndGet() > max) - throw new RuntimeException("visited too many files/dirs"); + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { + assertFalse(cnt.incrementAndGet() > max, "visited too many files/dirs"); dirs.remove(dir.toString()); return FileVisitResult.CONTINUE; } }); - if (cnt.get() != max || dirs.size() != 0 || files.size() != 0) - throw new RuntimeException("walk files/dirs failed"); - + assertFalse(cnt.get() != max || dirs.size() != 0 || files.size() != 0, + "walk files/dirs failed"); } finally { Files.deleteIfExists(path); } @@ -119,15 +117,15 @@ public class ZFSTests { zos.write("/fooo/bar".getBytes()); } - try (FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + try (FileSystem fs = FileSystems.newFileSystem(uri, Map.of())) { Files.walkFileTree(fs.getRootDirectories().iterator().next(), new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { files.remove(file.toString()); - if (!Arrays.equals(Files.readAllBytes(file), file.toString().getBytes())) - throw new RuntimeException("visited files has wrong content: " + file); + assertArrayEquals(file.toString().getBytes(), Files.readAllBytes(file), + "visited files has wrong content: " + file); return FileVisitResult.CONTINUE; } @Override @@ -137,8 +135,8 @@ public class ZFSTests { return FileVisitResult.CONTINUE; } }); - if (dirs.size() != 0 || files.size() != 0) - throw new RuntimeException("walk files/dirs failed"); + assertFalse(dirs.size() != 0 || files.size() != 0, + "walk files/dirs failed"); // for next test: updated any entry, the result zipfs file should have no // absolute path entry @@ -151,22 +149,22 @@ public class ZFSTests { files.add("/foo"); files.add("/bar"); files.add("/fooo/bar"); - try (FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + try (FileSystem fs = FileSystems.newFileSystem(uri, Map.of())) { Files.walk(fs.getPath("/")).forEach( p -> { if (Files.isDirectory(p)) { dirs.remove(p.toString()); } else { files.remove(p.toString()); try { - if (!Arrays.equals(Files.readAllBytes(p), p.toString().getBytes())) - throw new RuntimeException("visited files has wrong content: " + p); + assertArrayEquals(p.toString().getBytes(), Files.readAllBytes(p), + "visited files has wrong content: " + p); } catch (IOException x) { throw new RuntimeException(x); } } }); - if (dirs.size() != 0 || files.size() != 0) - throw new RuntimeException("walk files/dirs failed"); + assertFalse(dirs.size() != 0 || files.size() != 0, + "walk files/dirs failed"); } // updated zip file should not have "/" and entry with absolute path @@ -175,17 +173,16 @@ public class ZFSTests { .map(ZipEntry::toString) .sorted() .toArray(String[]::new); - if (!Arrays.equals(entries, new String[] {"bar", "foo", "fooo/", "fooo/bar" })) { - System.out.println("unexpeded: " + Arrays.toString(entries)); - throw new RuntimeException("unexpected entreis in updated zipfs file"); - } + assertArrayEquals(new String[] {"bar", "foo", "fooo/", "fooo/bar" }, entries, + "unexpected entreis in updated zipfs file"); } } finally { Files.deleteIfExists(path); } } - static void test7156873() throws Throwable { + @Test + void test7156873() throws Throwable { String DIRWITHSPACE = "testdir with spaces"; Path dir = Paths.get(DIRWITHSPACE); Path path = Paths.get(DIRWITHSPACE, "file.zip"); @@ -201,7 +198,8 @@ public class ZFSTests { } } - static void test8061777() throws Throwable { + @Test + void test8061777() throws Throwable { Path path = Paths.get("file.zip"); try { URI uri = URI.create("jar:" + path.toUri()); @@ -212,24 +210,25 @@ public class ZFSTests { FileSystemProvider fsp = fs.provider(); Path p = fs.getPath("/\u8868\u7533.txt"); // 0x95 0x5c 0x90 0x5c try (OutputStream os = fsp.newOutputStream(p)) { - os.write("Hello!".getBytes("ASCII")); + os.write("Hello!".getBytes(ASCII)); } Path dir = fs.getPath("/"); Files.list(dir) .forEach( child -> { System.out.println("child:" + child); - if (!child.toString().equals(p.toString())) - throw new RuntimeException("wrong path name created"); + assertEquals(p.toString(), child.toString(), + "wrong path name created"); }); - if (!"Hello!".equals(new String(Files.readAllBytes(p), "ASCII"))) - throw new RuntimeException("wrong content in newly created file"); + assertEquals("Hello!", new String(Files.readAllBytes(p), ASCII), + "wrong content in newly created file"); } } finally { Files.deleteIfExists(path); } } - static void tests() throws Throwable { + @Test + void tests() throws Throwable { Path path = Paths.get("file.zip"); try { URI uri = URI.create("jar:" + path.toUri()); @@ -245,16 +244,14 @@ public class ZFSTests { StandardOpenOption.WRITE, StandardOpenOption.APPEND); try (FileChannel ch = fsp.newFileChannel(p, options)) { - ch.write(ByteBuffer.wrap("Hello!".getBytes("ASCII"))); + ch.write(ByteBuffer.wrap("Hello!".getBytes(ASCII))); } // 8034773 try (OutputStream os = fsp.newOutputStream(p, new OpenOption[0])) { - os.write("Hello2!".getBytes("ASCII")); - } - if (!"Hello2!".equals(new String( - Files.readAllBytes(fs.getPath("test.txt"))))) { - throw new RuntimeException("failed to open as truncate_existing"); + os.write("Hello2!".getBytes(ASCII)); } + assertEquals("Hello2!", new String( + Files.readAllBytes(fs.getPath("test.txt"))), "failed to open as truncate_existing"); options = EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.APPEND, @@ -273,8 +270,8 @@ public class ZFSTests { Files.list(dir) .forEach( child -> { System.out.println("child:" + child); - if (child.toString().endsWith("/")) - throw new RuntimeException("subdir names ends with /"); + assertFalse(child.toString().endsWith("/"), + "subdir names ends with /"); }); } } finally { diff --git a/test/jdk/jdk/nio/zipfs/ZeroDate.java b/test/jdk/jdk/nio/zipfs/ZeroDate.java index ff702f63488..9c7f3abef0d 100644 --- a/test/jdk/jdk/nio/zipfs/ZeroDate.java +++ b/test/jdk/jdk/nio/zipfs/ZeroDate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -26,6 +26,7 @@ import static java.util.zip.ZipFile.CENTIM; import static java.util.zip.ZipFile.ENDHDR; import static java.util.zip.ZipFile.ENDOFF; import static java.util.zip.ZipFile.LOCTIM; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.io.InputStream; @@ -40,11 +41,17 @@ import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; -import java.util.Collections; +import java.util.Map; +import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import jdk.test.lib.Utils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; /* @test * @bug 8184940 8186227 8188869 @@ -53,42 +60,54 @@ import jdk.test.lib.Utils; * @author Liam Miller-Cushon * @modules jdk.zipfs * @library /test/lib + * @run junit ZeroDate */ public class ZeroDate { - public static void main(String[] args) throws Exception { - // create a zip file, and read it in as a byte array - Path path = Utils.createTempFile("bad", ".zip"); - try { - try (OutputStream os = Files.newOutputStream(path); - ZipOutputStream zos = new ZipOutputStream(os)) { - ZipEntry e = new ZipEntry("x"); - zos.putNextEntry(e); - zos.write((int) 'x'); - } - int len = (int) Files.size(path); - byte[] data = new byte[len]; - try (InputStream is = Files.newInputStream(path)) { - is.read(data); - } + static Path path; + static byte[] data; - // year, month, day are zero - testDate(data.clone(), 0, LocalDate.of(1979, 11, 30).atStartOfDay()); - // only year is zero - testDate(data.clone(), 0 << 25 | 4 << 21 | 5 << 16, LocalDate.of(1980, 4, 5).atStartOfDay()); - // month is greater than 12 - testDate(data.clone(), 0 << 25 | 13 << 21 | 1 << 16, LocalDate.of(1981, 1, 1).atStartOfDay()); - // 30th of February - testDate(data.clone(), 0 << 25 | 2 << 21 | 30 << 16, LocalDate.of(1980, 3, 1).atStartOfDay()); - // 30th of February, 24:60:60 - testDate(data.clone(), 0 << 25 | 2 << 21 | 30 << 16 | 24 << 11 | 60 << 5 | 60 >> 1, - LocalDateTime.of(1980, 3, 2, 1, 1, 0)); - } finally { - Files.delete(path); + @BeforeAll + static void setup() throws IOException { + // create a zip file, and read it in as a byte array + path = Utils.createTempFile("bad", ".zip"); + try (OutputStream os = Files.newOutputStream(path); + ZipOutputStream zos = new ZipOutputStream(os)) { + ZipEntry e = new ZipEntry("x"); + zos.putNextEntry(e); + zos.write((int) 'x'); + } + int len = (int) Files.size(path); + data = new byte[len]; + try (InputStream is = Files.newInputStream(path)) { + is.read(data); } } - private static void testDate(byte[] data, int date, LocalDateTime expected) throws IOException { + @AfterAll + static void cleanup () throws IOException { + Files.delete(path); + } + + static Stream dateData() { + return Stream.of( + // year, month, day are zero + Arguments.of(data.clone(), 0, LocalDate.of(1979, 11, 30).atStartOfDay()), + // only year is zero + Arguments.of(data.clone(), 0 << 25 | 4 << 21 | 5 << 16, LocalDate.of(1980, 4, 5).atStartOfDay()), + // month is greater than 12 + Arguments.of(data.clone(), 0 << 25 | 13 << 21 | 1 << 16, LocalDate.of(1981, 1, 1).atStartOfDay()), + // 30th of February + Arguments.of(data.clone(), 0 << 25 | 2 << 21 | 30 << 16, LocalDate.of(1980, 3, 1).atStartOfDay()), + // 30th of February, 24:60:60 + Arguments.of(data.clone(), 0 << 25 | 2 << 21 | 30 << 16 | 24 << 11 | 60 << 5 | 60 >> 1, + LocalDateTime.of(1980, 3, 2, 1, 1, 0)) + ); + } + + @ParameterizedTest + @MethodSource("dateData") + void testDate(byte[] data, int date, LocalDateTime expected) throws IOException { // set the datetime int endpos = data.length - ENDHDR; int cenpos = u16(data, endpos + ENDOFF); @@ -102,7 +121,7 @@ public class ZeroDate { os.write(data); } URI uri = URI.create("jar:" + path.toUri()); - try (FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + try (FileSystem fs = FileSystems.newFileSystem(uri, Map.of())) { Path entry = fs.getPath("x"); Instant actualInstant = Files.readAttributes(entry, BasicFileAttributes.class) @@ -110,10 +129,8 @@ public class ZeroDate { .toInstant(); Instant expectedInstant = expected.atZone(ZoneId.systemDefault()).toInstant(); - if (!actualInstant.equals(expectedInstant)) { - throw new AssertionError( - String.format("actual: %s, expected: %s", actualInstant, expectedInstant)); - } + assertEquals(expectedInstant, actualInstant, + String.format("actual: %s, expected: %s", actualInstant, expectedInstant)); } finally { Files.delete(path); } diff --git a/test/jdk/jdk/nio/zipfs/ZipFSOutputStreamTest.java b/test/jdk/jdk/nio/zipfs/ZipFSOutputStreamTest.java index 8175eec070d..4e797b57380 100644 --- a/test/jdk/jdk/nio/zipfs/ZipFSOutputStreamTest.java +++ b/test/jdk/jdk/nio/zipfs/ZipFSOutputStreamTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -22,11 +22,6 @@ * */ -import org.testng.Assert; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; import java.io.InputStream; @@ -37,6 +32,16 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; import java.util.Map; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -44,7 +49,7 @@ import java.util.Map; * @summary Verify that the outputstream created for zip file entries, through the ZipFileSystem * works fine for varying sizes of the zip file entries * @bug 8190753 8011146 8279536 - * @run testng/timeout=300 ZipFSOutputStreamTest + * @run junit/timeout=300 ZipFSOutputStreamTest */ public class ZipFSOutputStreamTest { // List of files to be added to the ZIP file along with their sizes in bytes @@ -56,12 +61,12 @@ public class ZipFSOutputStreamTest { private static final Path ZIP_FILE = Path.of("zipfs-outputstream-test.zip"); - @BeforeMethod + @BeforeEach public void setUp() throws IOException { deleteFiles(); } - @AfterMethod + @AfterEach public void tearDown() throws IOException { deleteFiles(); } @@ -70,13 +75,12 @@ public class ZipFSOutputStreamTest { Files.deleteIfExists(ZIP_FILE); } - @DataProvider(name = "zipFSCreationEnv") - private Object[][] zipFSCreationEnv() { - return new Object[][]{ - {Map.of("create", "true", "noCompression", "true")}, // STORED - {Map.of("create", "true", "noCompression", "false")} // DEFLATED + private static Stream zipFSCreationEnv() { + return Stream.of( + Arguments.of(Map.of("create", "true", "noCompression", "true")), // STORED + Arguments.of(Map.of("create", "true", "noCompression", "false")) // DEFLATED - }; + ); } /** @@ -84,7 +88,8 @@ public class ZipFSOutputStreamTest { * by the ZipFileSystem. Then verify that the generated zip file entries are as expected, * both in size and content */ - @Test(dataProvider = "zipFSCreationEnv") + @ParameterizedTest + @MethodSource("zipFSCreationEnv") public void testOutputStream(final Map env) throws Exception { final byte[] chunk = new byte[1024]; // fill it with some fixed content (the fixed content will later on help ease @@ -117,12 +122,12 @@ public class ZipFSOutputStreamTest { while ((numRead = is.read(buf)) != -1) { totalRead += numRead; // verify the content - Assert.assertEquals(Arrays.mismatch(buf, 0, numRead, chunk, 0, numRead), -1, + assertEquals(-1, Arrays.mismatch(buf, 0, numRead, chunk, 0, numRead), "Unexpected content in " + entryPath); } System.out.println("Read entry " + entryPath + " of bytes " + totalRead + " in " + (System.currentTimeMillis() - start) + " milli seconds"); - Assert.assertEquals(totalRead, (long) entry.getValue(), + assertEquals((long) entry.getValue(), totalRead, "Unexpected number of bytes read from zip entry " + entryPath); } } diff --git a/test/jdk/jdk/nio/zipfs/ZipFSPermissionsTest.java b/test/jdk/jdk/nio/zipfs/ZipFSPermissionsTest.java index d5e0a03a9fa..24cba1f301b 100644 --- a/test/jdk/jdk/nio/zipfs/ZipFSPermissionsTest.java +++ b/test/jdk/jdk/nio/zipfs/ZipFSPermissionsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -22,8 +22,6 @@ * */ -import org.testng.SkipException; -import org.testng.annotations.*; import java.io.IOException; import java.nio.file.FileSystem; @@ -36,9 +34,21 @@ import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; import java.util.Map; import java.util.Set; +import java.util.stream.Stream; import static java.nio.file.attribute.PosixFilePermission.*; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullSource; /** * @test @@ -46,7 +56,7 @@ import static org.testng.Assert.assertEquals; * @summary Updating an existing zip file does not preserve original permissions * @library /test/lib * @modules jdk.zipfs - * @run testng/othervm ZipFSPermissionsTest + * @run junit/othervm ZipFSPermissionsTest */ public class ZipFSPermissionsTest { @@ -62,18 +72,14 @@ public class ZipFSPermissionsTest { /** * Create the files used by the test */ - @BeforeSuite - public void setUp() throws Exception { + @BeforeAll + public static void setUp() throws Exception { boolean supportsPosix = FileSystems.getDefault() .supportedFileAttributeViews().contains("posix"); - // Check to see if File System supports POSIX permissions - if (supportsPosix) { - System.out.println("File Store Supports Posix"); - } else { - // As there is no POSIX permission support, skip running the test - throw new SkipException("Cannot set permissions on this File Store"); - } + + Assumptions.assumeTrue(supportsPosix, "Cannot set permissions on this File Store"); + System.out.println("File Store Supports Posix"); Files.writeString(entry0, "Tennis Pro"); Files.writeString(entry1, "Tennis is a lifetime sport!"); } @@ -81,7 +87,7 @@ public class ZipFSPermissionsTest { /** * Re-create the initial Zip file prior to each run. */ - @BeforeMethod + @BeforeEach public void before() throws Exception { Files.deleteIfExists(zipFile); zip(zipFile, Map.of("create", "true"), entry0); @@ -90,7 +96,7 @@ public class ZipFSPermissionsTest { /** * Remove Zip file used by test after each run. */ - @AfterMethod + @AfterEach public void tearDown() throws Exception { Files.deleteIfExists(zipFile); } @@ -98,8 +104,8 @@ public class ZipFSPermissionsTest { /** * Remove files used by test as part of final test run clean-up */ - @AfterSuite - public void suiteCleanUp() throws Exception { + @AfterAll + public static void suiteCleanUp() throws Exception { Files.deleteIfExists(zipFile); Files.deleteIfExists(entry0); Files.deleteIfExists(entry1); @@ -112,7 +118,9 @@ public class ZipFSPermissionsTest { * file * @throws Exception If an error occurs */ - @Test(dataProvider = "posixPermissions") + @ParameterizedTest + @MethodSource("posixPermissions") + @NullSource public void testZipPerms(Set newPerms) throws Exception { if (DEBUG) { System.out.printf("Test Run with perms= %s%n", newPerms); @@ -138,7 +146,7 @@ public class ZipFSPermissionsTest { // Zip file PosixFileAttributes afterAttrs = getPosixAttributes(zipFile); displayPermissions("Permissions after updating the Zip File", zipFile); - assertEquals(afterAttrs.permissions(), newPerms, + assertEquals(newPerms, afterAttrs.permissions(), "Permissions were not updated as expected!"); } @@ -206,23 +214,21 @@ public class ZipFSPermissionsTest { } /* - * DataProvider used to verify the permissions on a Zip file + * MethodSource used to verify the permissions on a Zip file * are as expected after updating the Zip file */ - @DataProvider(name = "posixPermissions") - private Object[][] posixPermissions() { - return new Object[][]{ - {null}, - {Set.of(OWNER_READ, OWNER_WRITE, OTHERS_READ)}, - {Set.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE)}, - {Set.of(OWNER_READ, OWNER_WRITE, OTHERS_READ, OTHERS_WRITE)}, - {Set.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, OTHERS_READ, - OTHERS_WRITE, OTHERS_EXECUTE)}, - {Set.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, + private static Stream posixPermissions() { + return Stream.of( + Arguments.of(Set.of(OWNER_READ, OWNER_WRITE, OTHERS_READ)), + Arguments.of(Set.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE)), + Arguments.of(Set.of(OWNER_READ, OWNER_WRITE, OTHERS_READ, OTHERS_WRITE)), + Arguments.of(Set.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, OTHERS_READ, + OTHERS_WRITE, OTHERS_EXECUTE)), + Arguments.of(Set.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, GROUP_READ, GROUP_WRITE,GROUP_EXECUTE, OTHERS_READ, - OTHERS_WRITE, OTHERS_EXECUTE)}, - {Set.of(OWNER_READ, OWNER_WRITE, GROUP_READ, GROUP_WRITE, - OTHERS_READ, OTHERS_WRITE)}, - }; + OTHERS_WRITE, OTHERS_EXECUTE)), + Arguments.of(Set.of(OWNER_READ, OWNER_WRITE, GROUP_READ, GROUP_WRITE, + OTHERS_READ, OTHERS_WRITE)) + ); } } diff --git a/test/jdk/jdk/nio/zipfs/ZipFSTester.java b/test/jdk/jdk/nio/zipfs/ZipFSTester.java index e6300a90004..84135fd0d7d 100644 --- a/test/jdk/jdk/nio/zipfs/ZipFSTester.java +++ b/test/jdk/jdk/nio/zipfs/ZipFSTester.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2026, 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 @@ -21,6 +21,9 @@ * questions. */ +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -68,6 +71,13 @@ import java.util.zip.ZipOutputStream; import static java.nio.file.StandardOpenOption.*; import static java.nio.file.StandardCopyOption.*; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /* * Tests various zipfs operations. @@ -78,57 +88,46 @@ import static java.nio.file.StandardCopyOption.*; * 8131067 8034802 8210899 8273961 8271079 8299864 * @summary Test Zip filesystem provider * @modules jdk.zipfs - * @run main ZipFSTester - * @run main/othervm ZipFSTester + * @run junit ZipFSTester + * @run junit/othervm ZipFSTester */ - public class ZipFSTester { - public static void main(String[] args) throws Exception { - // create JAR file for test, actual contents don't matter - Path jarFile = Utils.createJarFile("tester.jar", + + private static final Random RDM = new Random(); + + // create JAR file for test, actual contents don't matter + static Path jarFile; + + @BeforeAll + static void setup() throws Exception { + jarFile = Utils.createJarFile("tester.jar", "META-INF/MANIFEST.MF", "dir1/foo", "dir2/bar", "dir1/dir3/fooo"); - - try (FileSystem fs = newZipFileSystem(jarFile, Collections.emptyMap())) { - test0(fs); - test1(fs); - test2(fs); - testFileStoreNullArgs(fs); // more tests - } - testStreamChannel(); - testTime(jarFile); - test8069211(); - test8131067(); } - private static final Random RDM = new Random(); - - static void test0(FileSystem fs) - throws Exception - { + @Test + void test0() throws Exception { List list = new LinkedList<>(); - try (ZipFile zf = new ZipFile(fs.toString())) { + try (var fs = newZipFileSystem(jarFile, Map.of()); + ZipFile zf = new ZipFile(fs.toString())) { Enumeration zes = zf.entries(); while (zes.hasMoreElements()) { list.add(zes.nextElement().getName()); } for (String pname : list) { Path path = fs.getPath(pname); - if (!Files.exists(path)) - throw new RuntimeException("path existence check failed!"); + assertTrue(Files.exists(path), "path existence check failed!"); while ((path = path.getParent()) != null) { - if (!Files.exists(path)) - throw new RuntimeException("parent existence check failed!"); + assertTrue(Files.exists(path), "parent existence check failed!"); } } } } - static void test1(FileSystem fs0) - throws Exception - { + @Test + void test1() throws Exception { // prepare a src for testing Path src = getTempPath(); String tmpName = src.toString(); @@ -142,11 +141,12 @@ public class ZipFSTester { Path tmpfsPath = getTempPath(); Map env = new HashMap(); env.put("create", "true"); - try (FileSystem copy = newZipFileSystem(tmpfsPath, env)) { - z2zcopy(fs0, copy, "/", 0); + try (var fs = newZipFileSystem(jarFile, Map.of()); + FileSystem copy = newZipFileSystem(tmpfsPath, env)) { + z2zcopy(fs, copy, "/", 0); // copy the test jar itself in - Files.copy(Paths.get(fs0.toString()), copy.getPath("/foo.jar")); + Files.copy(Paths.get(fs.toString()), copy.getPath("/foo.jar")); Path zpath = copy.getPath("/foo.jar"); try (FileSystem zzfs = FileSystems.newFileSystem(zpath)) { Files.copy(src, zzfs.getPath("/srcInjarjar")); @@ -158,23 +158,15 @@ public class ZipFSTester { FileSystemProvider provider = fs.provider(); // newFileSystem(path...) should not throw exception try (FileSystem fsPath = provider.newFileSystem(tmpfsPath, new HashMap())){} - try (FileSystem fsUri = provider.newFileSystem( - new URI("jar", tmpfsPath.toUri().toString(), null), - new HashMap())) - { - throw new RuntimeException("newFileSystem(URI...) does not throw exception"); - } catch (FileSystemAlreadyExistsException fsaee) {} - - try { - provider.newFileSystem(new File(System.getProperty("test.src", ".")).toPath(), - new HashMap()); - throw new RuntimeException("newFileSystem() opens a directory as zipfs"); - } catch (UnsupportedOperationException uoe) {} - - try { - provider.newFileSystem(src, new HashMap()); - throw new RuntimeException("newFileSystem() opens a non-zip file as zipfs"); - } catch (UnsupportedOperationException uoe) {} + assertThrows(FileSystemAlreadyExistsException.class, + () -> provider.newFileSystem(new URI("jar", tmpfsPath.toUri().toString(), null), + new HashMap<>()), "newFileSystem(URI...) does not throw exception"); + assertThrows(UnsupportedOperationException.class, + () -> provider.newFileSystem(new File(System.getProperty("test.src", ".")).toPath(), + new HashMap<>()), "newFileSystem() opens a directory as zipfs"); + assertThrows(UnsupportedOperationException.class, + () -> provider.newFileSystem(src, new HashMap()), + "newFileSystem() opens a non-zip file as zipfs"); // walk walk(fs.getPath("/")); @@ -193,15 +185,13 @@ public class ZipFSTester { // delete Files.delete(dst); - if (Files.exists(dst)) - throw new RuntimeException("Failed!"); + assertFalse(Files.exists(dst)); // moveout Path dst3 = Paths.get(tmpName + "_Tmp"); Files.move(dst2, dst3); checkEqual(src, dst3); - if (Files.exists(dst2)) - throw new RuntimeException("Failed!"); + assertFalse(Files.exists(dst2)); // copyback + move Files.copy(dst3, dst); @@ -211,11 +201,9 @@ public class ZipFSTester { // delete Files.delete(dst4); - if (Files.exists(dst4)) - throw new RuntimeException("Failed!"); + assertFalse(Files.exists(dst4)); Files.delete(dst3); - if (Files.exists(dst3)) - throw new RuntimeException("Failed!"); + assertFalse(Files.exists(dst3)); // move (existing entry) Path dst5 = fs.getPath("META-INF/MANIFEST.MF"); @@ -227,12 +215,7 @@ public class ZipFSTester { // newInputStream on dir Path parent = dst2.getParent(); - try { - Files.newInputStream(parent); - throw new RuntimeException("Failed"); - } catch (FileSystemException e) { - // expected fse - } + assertThrows(FileSystemException.class, () -> Files.newInputStream(parent)); // rmdirs try { @@ -278,8 +261,8 @@ public class ZipFSTester { } } - static void test2(FileSystem fs) throws Exception { - + @Test + void test2() throws Exception { Path fs1Path = getTempPath(); Path fs2Path = getTempPath(); Path fs3Path = getTempPath(); @@ -293,129 +276,137 @@ public class ZipFSTester { final FileSystem fs3 = newZipFileSystem(fs3Path, env); System.out.println("copy src: fs -> fs0..."); - z2zcopy(fs, fs0, "/", 0); // copy fs -> fs1 - fs0.close(); // dump to file + try (var fs = newZipFileSystem(jarFile, Map.of())) { + z2zcopy(fs, fs0, "/", 0); // copy fs -> fs1 + fs0.close(); // dump to file - System.out.println("open fs0 as fs1"); - env = new HashMap(); - final FileSystem fs1 = newZipFileSystem(fs1Path, env); + System.out.println("open fs0 as fs1"); + env = new HashMap(); + final FileSystem fs1 = newZipFileSystem(fs1Path, env); - System.out.println("listing..."); - final ArrayList files = new ArrayList<>(); - final ArrayList dirs = new ArrayList<>(); - list(fs1.getPath("/"), files, dirs); + System.out.println("listing..."); + final ArrayList files = new ArrayList<>(); + final ArrayList dirs = new ArrayList<>(); + list(fs1.getPath("/"), files, dirs); - Thread t0 = new Thread(new Runnable() { - public void run() { - List list = new ArrayList<>(dirs); - Collections.shuffle(list); - for (String path : list) { - try { - z2zcopy(fs1, fs2, path, 0); - } catch (Exception x) { - x.printStackTrace(); - } - } - } - - }); - - Thread t1 = new Thread(new Runnable() { - public void run() { - List list = new ArrayList<>(dirs); - Collections.shuffle(list); - for (String path : list) { - try { - z2zcopy(fs1, fs2, path, 1); - } catch (Exception x) { - x.printStackTrace(); - } - } - } - - }); - - Thread t2 = new Thread(new Runnable() { - public void run() { - List list = new ArrayList<>(dirs); - Collections.shuffle(list); - for (String path : list) { - try { - z2zcopy(fs1, fs2, path, 2); - } catch (Exception x) { - x.printStackTrace(); - } - } - } - - }); - - Thread t3 = new Thread(new Runnable() { - public void run() { - List list = new ArrayList<>(files); - Collections.shuffle(list); - while (!list.isEmpty()) { - Iterator itr = list.iterator(); - while (itr.hasNext()) { - String path = itr.next(); + Thread t0 = new Thread(new Runnable() { + public void run() { + List list = new ArrayList<>(dirs); + Collections.shuffle(list); + for (String path : list) { try { - if (Files.exists(fs2.getPath(path))) { - z2zmove(fs2, fs3, path); - itr.remove(); - } - } catch (FileAlreadyExistsException x) { - itr.remove(); + z2zcopy(fs1, fs2, path, 0); } catch (Exception x) { x.printStackTrace(); } } } + + }); + + Thread t1 = new Thread(new Runnable() { + public void run() { + List list = new ArrayList<>(dirs); + Collections.shuffle(list); + for (String path : list) { + try { + z2zcopy(fs1, fs2, path, 1); + } catch (Exception x) { + x.printStackTrace(); + } + } + } + + }); + + Thread t2 = new Thread(new Runnable() { + public void run() { + List list = new ArrayList<>(dirs); + Collections.shuffle(list); + for (String path : list) { + try { + z2zcopy(fs1, fs2, path, 2); + } catch (Exception x) { + x.printStackTrace(); + } + } + } + + }); + + Thread t3 = new Thread(new Runnable() { + public void run() { + List list = new ArrayList<>(files); + Collections.shuffle(list); + while (!list.isEmpty()) { + Iterator itr = list.iterator(); + while (itr.hasNext()) { + String path = itr.next(); + try { + if (Files.exists(fs2.getPath(path))) { + z2zmove(fs2, fs3, path); + itr.remove(); + } + } catch (FileAlreadyExistsException x) { + itr.remove(); + } catch (Exception x) { + x.printStackTrace(); + } + } + } + } + + }); + + System.out.println("copying/removing..."); + t0.start(); + t1.start(); + t2.start(); + t3.start(); + t0.join(); + t1.join(); + t2.join(); + t3.join(); + + System.out.println("closing: fs1, fs2"); + fs1.close(); + fs2.close(); + + int failed = 0; + System.out.println("checkEqual: fs vs fs3"); + for (String path : files) { + try { + checkEqual(fs.getPath(path), fs3.getPath(path)); + } catch (IOException x) { + //x.printStackTrace(); + failed++; + } } + System.out.println("closing: fs3"); + fs3.close(); - }); + System.out.println("opening: fs3 as fs4"); + FileSystem fs4 = newZipFileSystem(fs3Path, env); - System.out.println("copying/removing..."); - t0.start(); t1.start(); t2.start(); t3.start(); - t0.join(); t1.join(); t2.join(); t3.join(); - System.out.println("closing: fs1, fs2"); - fs1.close(); - fs2.close(); + ArrayList files2 = new ArrayList<>(); + ArrayList dirs2 = new ArrayList<>(); + list(fs4.getPath("/"), files2, dirs2); - int failed = 0; - System.out.println("checkEqual: fs vs fs3"); - for (String path : files) { - try { - checkEqual(fs.getPath(path), fs3.getPath(path)); - } catch (IOException x) { - //x.printStackTrace(); - failed++; + System.out.println("checkEqual: fs vs fs4"); + for (String path : files2) { + checkEqual(fs.getPath(path), fs4.getPath(path)); } + System.out.println("walking: fs4"); + walk(fs4.getPath("/")); + System.out.println("closing: fs4"); + fs4.close(); + System.out.printf("failed=%d%n", failed); + + Files.delete(fs1Path); + Files.delete(fs2Path); + Files.delete(fs3Path); } - System.out.println("closing: fs3"); - fs3.close(); - - System.out.println("opening: fs3 as fs4"); - FileSystem fs4 = newZipFileSystem(fs3Path, env); - - - ArrayList files2 = new ArrayList<>(); - ArrayList dirs2 = new ArrayList<>(); - list(fs4.getPath("/"), files2, dirs2); - - System.out.println("checkEqual: fs vs fs4"); - for (String path : files2) { - checkEqual(fs.getPath(path), fs4.getPath(path)); - } - System.out.println("walking: fs4"); - walk(fs4.getPath("/")); - System.out.println("closing: fs4"); - fs4.close(); - System.out.printf("failed=%d%n", failed); - - Files.delete(fs1Path); - Files.delete(fs2Path); - Files.delete(fs3Path); } static final int METHOD_STORED = 0; @@ -448,29 +439,21 @@ public class ZipFSTester { CRC32 crc32 = new CRC32(); crc32.update(expected); - if (((Long)Files.getAttribute(path, "zip:crc")).intValue() != - (int)crc32.getValue()) { - System.out.printf(" getAttribute.crc <%s> failed %x vs %x ...%n", + assertEquals((int)crc32.getValue(), ((Long)Files.getAttribute(path, "zip:crc")).intValue(), + " getAttribute.crc <%s> failed %x vs %x ...%n".formatted( path.toString(), ((Long)Files.getAttribute(path, "zip:crc")).intValue(), - (int)crc32.getValue()); - throw new RuntimeException("CHECK FAILED!"); - } + (int)crc32.getValue())); - if (((Long)Files.getAttribute(path, "zip:size")).intValue() != expected.length) { - System.out.printf(" getAttribute.size <%s> failed %x vs %x ...%n", + assertEquals(expected.length, ((Long)Files.getAttribute(path, "zip:size")).intValue(), + " getAttribute.size <%s> failed %x vs %x ...%n".formatted( path.toString(), ((Long)Files.getAttribute(path, "zip:size")).intValue(), - expected.length); - throw new RuntimeException("CHECK FAILED!"); - } + expected.length)); //streams try (InputStream is = Files.newInputStream(path)) { - if (!Arrays.equals(is.readAllBytes(), expected)) { - System.out.printf(" newInputStream <%s> failed...%n", path.toString()); - throw new RuntimeException("CHECK FAILED!"); - } + assertArrayEquals(expected, is.readAllBytes(), " newInputStream <%s> failed...%n".formatted(path.toString())); } // channels -- via sun.nio.ch.ChannelInputStream @@ -478,17 +461,12 @@ public class ZipFSTester { InputStream is = Channels.newInputStream(sbc)) { // check all bytes match - if (!Arrays.equals(is.readAllBytes(), expected)) { - System.out.printf(" newByteChannel <%s> failed...%n", path.toString()); - throw new RuntimeException("CHECK FAILED!"); - } + assertArrayEquals(expected, is.readAllBytes(), + " newByteChannel <%s> failed...%n".formatted(path.toString())); // Check if read position is at the end - if (sbc.position() != expected.length) { - System.out.printf("pos [%s]: size=%d, position=%d%n", - path.toString(), expected.length, sbc.position()); - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(expected.length, sbc.position(), "pos [%s]: size=%d, position=%d%n".formatted( + path.toString(), expected.length, sbc.position())); // Check position(x) + read() at the random/specific pos/len byte[] buf = new byte[1024]; @@ -503,21 +481,20 @@ public class ZipFSTester { // System.out.printf(" --> %d, %d%n", pos, len); bb.position(0).limit(len); // bb.flip().limit(len); int expectedReadResult = sbc.size() == 0 ? -1 : len; - if (sbc.position(pos).position() != pos || + assertFalse(sbc.position(pos).position() != pos || sbc.read(bb) != expectedReadResult || - !Arrays.equals(buf, 0, bb.position(), expected, pos, pos + len)) { - System.out.printf("read()/position() failed%n"); - throw new RuntimeException("CHECK FAILED!"); - } + !Arrays.equals(buf, 0, bb.position(), expected, pos, pos + len), + "read()/position() failed%n"); } } catch (IOException x) { x.printStackTrace(); - throw new RuntimeException("CHECK FAILED!"); + fail("CHECK FAILED!"); } } // test entry stream/channel reading - static void testStreamChannel() throws Exception { + @Test + void testStreamChannel() throws Exception { Path zpath = getTempPath(); try { var crc = new CRC32(); @@ -542,7 +519,7 @@ public class ZipFSTester { zos.closeEntry(); } } - try (var zfs = newZipFileSystem(zpath, Collections.emptyMap())) { + try (var zfs = newZipFileSystem(zpath, Map.of())) { for (Object[] e : entries) { Path path = zfs.getPath((String)e[0]); byte[] bytes = (byte[])e[2]; @@ -561,7 +538,7 @@ public class ZipFSTester { } } } - try (var zfs = newZipFileSystem(zpath, Collections.emptyMap())) { + try (var zfs = newZipFileSystem(zpath, Map.of())) { for (Object[] e : entries) { checkRead(zfs.getPath((String)e[0]), (byte[])e[2]); } @@ -574,7 +551,7 @@ public class ZipFSTester { Files.write(zfs.getPath((String)e[0]), (byte[])e[2]); } } - try (var zfs = newZipFileSystem(zpath, Collections.emptyMap())) { + try (var zfs = newZipFileSystem(zpath, Map.of())) { for (Object[] e : entries) { checkRead(zfs.getPath((String)e[0]), (byte[])e[2]); } @@ -591,7 +568,7 @@ public class ZipFSTester { } } } - try (var zfs = newZipFileSystem(zpath, Collections.emptyMap())) { + try (var zfs = newZipFileSystem(zpath, Map.of())) { for (Object[] e : entries) { checkRead(zfs.getPath((String)e[0]), (byte[])e[2]); } @@ -604,9 +581,10 @@ public class ZipFSTester { } // test file stamp - static void testTime(Path src) throws Exception { + @Test + void testTime() throws Exception { BasicFileAttributes attrs = Files - .getFileAttributeView(src, BasicFileAttributeView.class) + .getFileAttributeView(jarFile, BasicFileAttributeView.class) .readAttributes(); // create a new filesystem, copy this file into it Map env = new HashMap(); @@ -616,8 +594,8 @@ public class ZipFSTester { System.out.println("test copy with timestamps..."); // copyin Path dst = getPathWithParents(fs, "me"); - Files.copy(src, dst, COPY_ATTRIBUTES); - checkEqual(src, dst); + Files.copy(jarFile, dst, COPY_ATTRIBUTES); + checkEqual(jarFile, dst); System.out.println("mtime: " + attrs.lastModifiedTime()); System.out.println("ctime: " + attrs.creationTime()); System.out.println("atime: " + attrs.lastAccessTime()); @@ -630,20 +608,19 @@ public class ZipFSTester { System.out.println("atime: " + dstAttrs.lastAccessTime()); // 1-second granularity - if (attrs.lastModifiedTime().to(TimeUnit.SECONDS) != + assertFalse(attrs.lastModifiedTime().to(TimeUnit.SECONDS) != dstAttrs.lastModifiedTime().to(TimeUnit.SECONDS) || attrs.lastAccessTime().to(TimeUnit.SECONDS) != dstAttrs.lastAccessTime().to(TimeUnit.SECONDS) || attrs.creationTime().to(TimeUnit.SECONDS) != - dstAttrs.creationTime().to(TimeUnit.SECONDS)) { - throw new RuntimeException("Timestamp Copy Failed!"); - } + dstAttrs.creationTime().to(TimeUnit.SECONDS), "Timestamp Copy Failed!"); } finally { Files.delete(fsPath); } } - static void test8069211() throws Exception { + @Test + void test8069211() throws Exception { // create a new filesystem, copy this file into it Map env = new HashMap(); env.put("create", "true"); @@ -655,18 +632,17 @@ public class ZipFSTester { out.close(); } try (FileSystem fs = newZipFileSystem(fsPath, new HashMap())) { - if (!Arrays.equals(Files.readAllBytes(fs.getPath("/foo")), - "hello".getBytes())) { - throw new RuntimeException("entry close() failed"); - } + assertArrayEquals("hello".getBytes(), Files.readAllBytes(fs.getPath("/foo")), + "entry close() failed"); } catch (Exception x) { - throw new RuntimeException("entry close() failed", x); + fail("entry close() failed", x); } finally { Files.delete(fsPath); } } - static void test8131067() throws Exception { + @Test + void test8131067() throws Exception { Map env = new HashMap(); env.put("create", "true"); @@ -677,10 +653,8 @@ public class ZipFSTester { try (FileSystem fs = newZipFileSystem(fsPath, env);) { Files.write(fs.getPath("/foo"), "hello".getBytes()); URI fooUri = fs.getPath("/foo").toUri(); - if (!Arrays.equals(Files.readAllBytes(Paths.get(fooUri)), - "hello".getBytes())) { - throw new RuntimeException("entry close() failed"); - } + assertArrayEquals("hello".getBytes(), Files.readAllBytes(Paths.get(fooUri)), + "entry close() failed"); } finally { Files.delete(fsPath); } @@ -868,19 +842,13 @@ public class ZipFSTester { int nDst = 0; while (nDst < nSrc) { int n = isDst.read(bufDst, nDst, nSrc - nDst); - if (n == -1) { - System.out.printf("checking <%s> vs <%s>...%n", - src.toString(), dst.toString()); - throw new RuntimeException("CHECK FAILED!"); - } + assertNotEquals(-1, n, "checking <%s> vs <%s>...%n".formatted( + src.toString(), dst.toString())); nDst += n; } while (--nSrc >= 0) { - if (bufSrc[nSrc] != bufDst[nSrc]) { - System.out.printf("checking <%s> vs <%s>...%n", - src.toString(), dst.toString()); - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(bufSrc[nSrc], bufDst[nSrc], "checking <%s> vs <%s>...%n".formatted( + src.toString(), dst.toString())); nSrc--; } } @@ -890,29 +858,20 @@ public class ZipFSTester { try (SeekableByteChannel chSrc = Files.newByteChannel(src); SeekableByteChannel chDst = Files.newByteChannel(dst)) { - if (chSrc.size() != chDst.size()) { - System.out.printf("src[%s].size=%d, dst[%s].size=%d%n", + assertEquals(chSrc.size(), chDst.size(), "src[%s].size=%d, dst[%s].size=%d%n".formatted( chSrc.toString(), chSrc.size(), - chDst.toString(), chDst.size()); - throw new RuntimeException("CHECK FAILED!"); - } + chDst.toString(), chDst.size())); ByteBuffer bbSrc = ByteBuffer.allocate(8192); ByteBuffer bbDst = ByteBuffer.allocate(8192); int nSrc = 0; while ((nSrc = chSrc.read(bbSrc)) != -1) { int nDst = chDst.read(bbDst); - if (nSrc != nDst) { - System.out.printf("checking <%s> vs <%s>...%n", - src.toString(), dst.toString()); - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(nSrc, nDst, + "checking <%s> vs <%s>...%n".formatted(src.toString(), dst.toString())); while (--nSrc >= 0) { - if (bbSrc.get(nSrc) != bbDst.get(nSrc)) { - System.out.printf("checking <%s> vs <%s>...%n", - src.toString(), dst.toString()); - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(bbSrc.get(nSrc), bbDst.get(nSrc), + "checking <%s> vs <%s>...%n".formatted(src.toString(), dst.toString())); nSrc--; } bbSrc.flip(); @@ -920,18 +879,12 @@ public class ZipFSTester { } // Check if source read position is at the end - if (chSrc.position() != chSrc.size()) { - System.out.printf("src[%s]: size=%d, position=%d%n", - chSrc.toString(), chSrc.size(), chSrc.position()); - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(chSrc.position(), chSrc.size(), "src[%s]: size=%d, position=%d%n".formatted( + chSrc.toString(), chSrc.size(), chSrc.position())); // Check if destination read position is at the end - if (chDst.position() != chDst.size()) { - System.out.printf("dst[%s]: size=%d, position=%d%n", - chDst.toString(), chDst.size(), chDst.position()); - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(chDst.position(), chDst.size(), "dst[%s]: size=%d, position=%d%n".formatted( + chDst.toString(), chDst.size(), chDst.position())); // Check position(x) + read() at the specific pos/len for (int i = 0; i < 10; i++) { @@ -996,18 +949,12 @@ public class ZipFSTester { } // Check if source read position is at the end - if (srcCh.position() != srcCh.size()) { - System.out.printf("src[%s]: size=%d, position=%d%n", - srcCh.toString(), srcCh.size(), srcCh.position()); - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(srcCh.position(), srcCh.size(), "src[%s]: size=%d, position=%d%n".formatted( + srcCh.toString(), srcCh.size(), srcCh.position())); // Check if destination write position is at the end - if (dstCh.position() != dstCh.size()) { - System.out.printf("dst[%s]: size=%d, position=%d%n", - dstCh.toString(), dstCh.size(), dstCh.position()); - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(dstCh.position(), dstCh.size(), "dst[%s]: size=%d, position=%d%n".formatted( + dstCh.toString(), dstCh.size(), dstCh.position())); } } @@ -1037,17 +984,13 @@ public class ZipFSTester { try (SeekableByteChannel sbc = Files.newByteChannel(path)) { System.out.printf(" sbc[0]: pos=%d, size=%d%n", sbc.position(), sbc.size()); - if (sbc.position() != 0) { - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(0, sbc.position()); bb = ByteBuffer.allocate((int)sbc.size()); n = sbc.read(bb); System.out.printf(" sbc[1]: read=%d, pos=%d, size=%d%n", n, sbc.position(), sbc.size()); - if (sbc.position() != sbc.size()) { - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(sbc.position(), sbc.size()); bb2 = ByteBuffer.allocate((int)sbc.size()); } @@ -1057,16 +1000,13 @@ public class ZipFSTester { sbc.position(N); System.out.printf(" sbc[2]: pos=%d, size=%d%n", sbc.position(), sbc.size()); - if (sbc.position() != N) { - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(N, sbc.position()); bb2.limit(100); n = sbc.read(bb2); System.out.printf(" sbc[3]: read=%d, pos=%d, size=%d%n", n, sbc.position(), sbc.size()); - if (n < 0 || sbc.position() != (N + n)) { - throw new RuntimeException("CHECK FAILED!"); - } + assertFalse(n < 0); + assertFalse(sbc.position() != (N + n)); System.out.printf(" sbc[4]: bb[%d]=%d, bb1[0]=%d%n", N, bb.get(N) & 0xff, bb2.get(0) & 0xff); } @@ -1086,38 +1026,22 @@ public class ZipFSTester { /** * Tests if certain methods throw a NullPointerException if invoked with null * as specified in java.nio.file.package-info.java - * @param fs file system containing at least one ZipFileStore * * @see 8299864 */ - static void testFileStoreNullArgs(FileSystem fs) { - FileStore store = fs.getFileStores().iterator().next(); + @Test + void testFileStoreNullArgs() throws Exception { + try (var fs = newZipFileSystem(jarFile, Map.of())) { + // file system containing at least one ZipFileStore + FileStore store = fs.getFileStores().iterator().next(); - // Make sure we are testing the right thing - if (!"jdk.nio.zipfs.ZipFileStore".equals(store.getClass().getName())) - throw new AssertionError(store.getClass().getName()); + // Make sure we are testing the right thing + assertEquals("jdk.nio.zipfs.ZipFileStore", store.getClass().getName()); - assertThrowsNPE(() -> store.supportsFileAttributeView((String) null)); - assertThrowsNPE(() -> store.supportsFileAttributeView((Class) null)); - assertThrowsNPE(() -> store.getAttribute(null)); - assertThrowsNPE(() -> store.getFileStoreAttributeView(null)); - } - - @FunctionalInterface - private interface ThrowingRunnable { - void run() throws Exception; - } - - static void assertThrowsNPE(ThrowingRunnable r) { - try { - r.run(); - // Didn't throw an exception - throw new AssertionError(); - } catch (NullPointerException expected) { - // happy path - } catch (Exception e) { - throw new AssertionError(e); + assertThrows(NullPointerException.class, () -> store.supportsFileAttributeView((String) null)); + assertThrows(NullPointerException.class, () -> store.supportsFileAttributeView((Class) null)); + assertThrows(NullPointerException.class, () -> store.getAttribute(null)); + assertThrows(NullPointerException.class, () -> store.getFileStoreAttributeView(null)); } } - } diff --git a/test/jdk/jdk/nio/zipfs/jarfs/JFSTester.java b/test/jdk/jdk/nio/zipfs/jarfs/JFSTester.java index 82505e24e6c..46bb27dcf10 100644 --- a/test/jdk/jdk/nio/zipfs/jarfs/JFSTester.java +++ b/test/jdk/jdk/nio/zipfs/jarfs/JFSTester.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -30,12 +30,9 @@ * @library /test/lib/ * @build jdk.test.lib.util.JarBuilder * jdk.test.lib.compiler.Compiler - * @run testng JFSTester + * @run junit JFSTester */ -import org.testng.Assert; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; import java.io.IOException; import java.io.UncheckedIOException; @@ -52,35 +49,41 @@ import java.util.stream.Collectors; import jdk.test.lib.util.JarBuilder; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + + public class JFSTester { - private URI jarURI; + private static URI jarURI; - final private String root_dir1_leaf1_txt = "This is leaf 1." + System.lineSeparator(); - final private String root_dir1_leaf2_txt = "This is leaf 2." + System.lineSeparator(); - final private String root_dir2_leaf3_txt = "This is leaf 3." + System.lineSeparator(); - final private String root_dir2_leaf4_txt = "This is leaf 4." + System.lineSeparator(); - final private String v9_root_dir2_leaf3_txt = "This is version 9 leaf 3." + System.lineSeparator(); - final private String v9_root_dir2_leaf4_txt = "This is version 9 leaf 4." + System.lineSeparator(); - final private String v9_root_dir3_leaf5_txt = "This is version 9 leaf 5." + System.lineSeparator(); - final private String v9_root_dir3_leaf6_txt = "This is version 9 leaf 6." + System.lineSeparator(); - final private String v10_root_dir3_leaf5_txt = "This is version 10 leaf 5." + System.lineSeparator(); - final private String v10_root_dir3_leaf6_txt = "This is version 10 leaf 6." + System.lineSeparator(); + private static final String ROOT_DIR_1_LEAF_1_TXT = "This is leaf 1." + System.lineSeparator(); + private static final String ROOT_DIR_1_LEAF_2_TXT = "This is leaf 2." + System.lineSeparator(); + private static final String ROOT_DIR_2_LEAF_3_TXT = "This is leaf 3." + System.lineSeparator(); + private static final String ROOT_DIR_2_LEAF_4_TXT = "This is leaf 4." + System.lineSeparator(); + private static final String V_9_ROOT_DIR_2_LEAF_3_TXT = "This is version 9 leaf 3." + System.lineSeparator(); + private static final String V_9_ROOT_DIR_2_LEAF_4_TXT = "This is version 9 leaf 4." + System.lineSeparator(); + private static final String V_9_ROOT_DIR_3_LEAF_5_TXT = "This is version 9 leaf 5." + System.lineSeparator(); + private static final String V_9_ROOT_DIR_3_LEAF_6_TXT = "This is version 9 leaf 6." + System.lineSeparator(); + private static final String V_10_ROOT_DIR_3_LEAF_5_TXT = "This is version 10 leaf 5." + System.lineSeparator(); + private static final String V_10_ROOT_DIR_3_LEAF_6_TXT = "This is version 10 leaf 6." + System.lineSeparator(); - @BeforeClass - public void initialize() throws Exception { + @BeforeAll + public static void initialize() throws Exception { Path jarfile = Paths.get("test.jar"); JarBuilder jb = new JarBuilder(jarfile.toString()); jb.addAttribute("Multi-Release", "true"); - jb.addEntry("root/dir1/leaf1.txt", root_dir1_leaf1_txt.getBytes()); - jb.addEntry("root/dir1/leaf2.txt", root_dir1_leaf2_txt.getBytes()); - jb.addEntry("root/dir2/leaf3.txt", root_dir2_leaf3_txt.getBytes()); - jb.addEntry("root/dir2/leaf4.txt", root_dir2_leaf4_txt.getBytes()); - jb.addEntry("META-INF/versions/9/root/dir2/leaf3.txt", v9_root_dir2_leaf3_txt.getBytes()); - jb.addEntry("META-INF/versions/9/root/dir2/leaf4.txt", v9_root_dir2_leaf4_txt.getBytes()); - jb.addEntry("META-INF/versions/9/root/dir3/leaf5.txt", v9_root_dir3_leaf5_txt.getBytes()); - jb.addEntry("META-INF/versions/9/root/dir3/leaf6.txt", v9_root_dir3_leaf6_txt.getBytes()); - jb.addEntry("META-INF/versions/10/root/dir3/leaf5.txt", v10_root_dir3_leaf5_txt.getBytes()); - jb.addEntry("META-INF/versions/10/root/dir3/leaf6.txt", v10_root_dir3_leaf6_txt.getBytes()); + jb.addEntry("root/dir1/leaf1.txt", ROOT_DIR_1_LEAF_1_TXT.getBytes()); + jb.addEntry("root/dir1/leaf2.txt", ROOT_DIR_1_LEAF_2_TXT.getBytes()); + jb.addEntry("root/dir2/leaf3.txt", ROOT_DIR_2_LEAF_3_TXT.getBytes()); + jb.addEntry("root/dir2/leaf4.txt", ROOT_DIR_2_LEAF_4_TXT.getBytes()); + jb.addEntry("META-INF/versions/9/root/dir2/leaf3.txt", V_9_ROOT_DIR_2_LEAF_3_TXT.getBytes()); + jb.addEntry("META-INF/versions/9/root/dir2/leaf4.txt", V_9_ROOT_DIR_2_LEAF_4_TXT.getBytes()); + jb.addEntry("META-INF/versions/9/root/dir3/leaf5.txt", V_9_ROOT_DIR_3_LEAF_5_TXT.getBytes()); + jb.addEntry("META-INF/versions/9/root/dir3/leaf6.txt", V_9_ROOT_DIR_3_LEAF_6_TXT.getBytes()); + jb.addEntry("META-INF/versions/10/root/dir3/leaf5.txt", V_10_ROOT_DIR_3_LEAF_5_TXT.getBytes()); + jb.addEntry("META-INF/versions/10/root/dir3/leaf6.txt", V_10_ROOT_DIR_3_LEAF_6_TXT.getBytes()); jb.build(); System.out.println("Created " + jarfile + ": " + Files.exists(jarfile)); jarURI = new URI("jar", jarfile.toUri().toString(), null); @@ -92,38 +95,38 @@ public class JFSTester { Map env = new HashMap<>(); Set contents = doTest(env); Set expectedContents = Set.of( - root_dir1_leaf1_txt, - root_dir1_leaf2_txt, - root_dir2_leaf3_txt, - root_dir2_leaf4_txt + ROOT_DIR_1_LEAF_1_TXT, + ROOT_DIR_1_LEAF_2_TXT, + ROOT_DIR_2_LEAF_3_TXT, + ROOT_DIR_2_LEAF_4_TXT ); - Assert.assertEquals(contents, expectedContents); + assertEquals(expectedContents, contents); // open file as multi-release for version 9 env.put("multi-release", "9"); contents = doTest(env); expectedContents = Set.of( - root_dir1_leaf1_txt, - root_dir1_leaf2_txt, - v9_root_dir2_leaf3_txt, - v9_root_dir2_leaf4_txt, - v9_root_dir3_leaf5_txt, - v9_root_dir3_leaf6_txt + ROOT_DIR_1_LEAF_1_TXT, + ROOT_DIR_1_LEAF_2_TXT, + V_9_ROOT_DIR_2_LEAF_3_TXT, + V_9_ROOT_DIR_2_LEAF_4_TXT, + V_9_ROOT_DIR_3_LEAF_5_TXT, + V_9_ROOT_DIR_3_LEAF_6_TXT ); - Assert.assertEquals(contents, expectedContents); + assertEquals(expectedContents, contents); // open file as multi-release for version 10 env.put("multi-release", "10"); contents = doTest(env); expectedContents = Set.of( - root_dir1_leaf1_txt, - root_dir1_leaf2_txt, - v9_root_dir2_leaf3_txt, - v9_root_dir2_leaf4_txt, - v10_root_dir3_leaf5_txt, - v10_root_dir3_leaf6_txt + ROOT_DIR_1_LEAF_1_TXT, + ROOT_DIR_1_LEAF_2_TXT, + V_9_ROOT_DIR_2_LEAF_3_TXT, + V_9_ROOT_DIR_2_LEAF_4_TXT, + V_10_ROOT_DIR_3_LEAF_5_TXT, + V_10_ROOT_DIR_3_LEAF_6_TXT ); - Assert.assertEquals(contents, expectedContents); + assertEquals(expectedContents, contents); } private Set doTest(Map env) throws IOException { @@ -158,7 +161,7 @@ public class JFSTester { "!/root/dir2/leaf3.txt", "!/root/dir2/leaf4.txt" ); - Assert.assertEquals(contents, expectedContents); + assertEquals(expectedContents, contents); // open file as multi-release for version 9 env.put("multi-release", "9"); @@ -171,7 +174,7 @@ public class JFSTester { "!/META-INF/versions/9/root/dir3/leaf5.txt", "!/META-INF/versions/9/root/dir3/leaf6.txt" ); - Assert.assertEquals(contents, expectedContents); + assertEquals(expectedContents, contents); // open file as multi-release for version 10 env.put("multi-release", "10"); @@ -184,7 +187,7 @@ public class JFSTester { "!/META-INF/versions/10/root/dir3/leaf5.txt", "!/META-INF/versions/10/root/dir3/leaf6.txt" ); - Assert.assertEquals(contents, expectedContents); + assertEquals(expectedContents, contents); } private Set doTestUri(Map env) throws IOException { diff --git a/test/jdk/jdk/nio/zipfs/jarfs/MultiReleaseJarTest.java b/test/jdk/jdk/nio/zipfs/jarfs/MultiReleaseJarTest.java index 1a336278089..ad0b47a75ee 100644 --- a/test/jdk/jdk/nio/zipfs/jarfs/MultiReleaseJarTest.java +++ b/test/jdk/jdk/nio/zipfs/jarfs/MultiReleaseJarTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -32,7 +32,7 @@ * @build CreateMultiReleaseTestJars * jdk.test.lib.util.JarBuilder * jdk.test.lib.compiler.Compiler - * @run testng MultiReleaseJarTest + * @run junit MultiReleaseJarTest */ import java.io.IOException; @@ -46,11 +46,22 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; -import org.testng.Assert; -import org.testng.annotations.*; import jdk.test.lib.util.JarBuilder; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class MultiReleaseJarTest { final private int MAJOR_VERSION = Runtime.version().feature(); private static final String PROPERTY_RELEASE_VERSION = "releaseVersion"; @@ -69,7 +80,7 @@ public class MultiReleaseJarTest { private URI mruri; private URI smruri; - @BeforeClass + @BeforeAll public void initialize() throws Exception { creator.compileEntries(); creator.buildUnversionedJar(); @@ -90,55 +101,51 @@ public class MultiReleaseJarTest { Files.delete(Paths.get(userdir, "short-multi-release.jar")); } - @DataProvider(name="strings") - public Object[][] createStrings() { - return new Object[][]{ - {"runtime", MAJOR_VERSION, "8"}, - {null, 8, Integer.toString(MAJOR_VERSION)}, - {"8", 8, "9"}, - {"9", 9, null}, - {Integer.toString(MAJOR_VERSION), MAJOR_VERSION, "8"}, - {Integer.toString(MAJOR_VERSION+1), MAJOR_VERSION, "8"}, - {"50", MAJOR_VERSION, "9"} - }; + public Stream createStrings() { + return Stream.of( + Arguments.of("runtime", MAJOR_VERSION, "8"), + Arguments.of(null, 8, Integer.toString(MAJOR_VERSION)), + Arguments.of("8", 8, "9"), + Arguments.of("9", 9, null), + Arguments.of(Integer.toString(MAJOR_VERSION), MAJOR_VERSION, "8"), + Arguments.of(Integer.toString(MAJOR_VERSION+1), MAJOR_VERSION, "8"), + Arguments.of("50", MAJOR_VERSION, "9") + ); } - @DataProvider(name="integers") - public Object[][] createIntegers() { - return new Object[][] { - {null, 8, Integer.valueOf(9)}, - {Integer.valueOf(8), 8, Integer.valueOf(9)}, - {Integer.valueOf(9), 9, Integer.valueOf(MAJOR_VERSION)}, - {Integer.valueOf(MAJOR_VERSION), MAJOR_VERSION, Integer.valueOf(8)}, - {Integer.valueOf(MAJOR_VERSION + 1), MAJOR_VERSION, null}, - {Integer.valueOf(100), MAJOR_VERSION, Integer.valueOf(8)} - }; + public Stream createIntegers() { + return Stream.of( + Arguments.of(null, 8, Integer.valueOf(9)), + Arguments.of(Integer.valueOf(8), 8, Integer.valueOf(9)), + Arguments.of(Integer.valueOf(9), 9, Integer.valueOf(MAJOR_VERSION)), + Arguments.of(Integer.valueOf(MAJOR_VERSION), MAJOR_VERSION, Integer.valueOf(8)), + Arguments.of(Integer.valueOf(MAJOR_VERSION + 1), MAJOR_VERSION, null), + Arguments.of(Integer.valueOf(100), MAJOR_VERSION, Integer.valueOf(8)) + ); } - @DataProvider(name="versions") - public Object[][] createVersions() { - return new Object[][] { - {null, 8, Version.parse("14")}, - {Version.parse("8"), 8, Version.parse("7")}, - {Version.parse("9"), 9, null}, - {Version.parse(Integer.toString(MAJOR_VERSION)), MAJOR_VERSION, Version.parse("8")}, - {Version.parse(Integer.toString(MAJOR_VERSION) + 1), MAJOR_VERSION, Version.parse("9")}, - {Version.parse("100"), MAJOR_VERSION, Version.parse("14")} - }; + public Stream createVersions() { + return Stream.of( + Arguments.of(null, 8, Version.parse("14")), + Arguments.of(Version.parse("8"), 8, Version.parse("7")), + Arguments.of(Version.parse("9"), 9, null), + Arguments.of(Version.parse(Integer.toString(MAJOR_VERSION)), MAJOR_VERSION, Version.parse("8")), + Arguments.of(Version.parse(Integer.toString(MAJOR_VERSION) + 1), MAJOR_VERSION, Version.parse("9")), + Arguments.of(Version.parse("100"), MAJOR_VERSION, Version.parse("14")) + ); } - @DataProvider(name="invalidVersions") - public Object[][] invalidVersions() { - return new Object[][] { - {Map.of(PROPERTY_RELEASE_VERSION, "")}, - {Map.of(PROPERTY_RELEASE_VERSION, "invalid")}, - {Map.of(PROPERTY_RELEASE_VERSION, "0")}, - {Map.of(PROPERTY_RELEASE_VERSION, "-1")}, - {Map.of(PROPERTY_RELEASE_VERSION, "11.0.1")}, - {Map.of(PROPERTY_RELEASE_VERSION, new ArrayList())}, - {Map.of(PROPERTY_RELEASE_VERSION, Integer.valueOf(0))}, - {Map.of(PROPERTY_RELEASE_VERSION, Integer.valueOf(-1))} - }; + public Stream invalidVersions() { + return Stream.of( + Arguments.of(Map.of(PROPERTY_RELEASE_VERSION, "")), + Arguments.of(Map.of(PROPERTY_RELEASE_VERSION, "invalid")), + Arguments.of(Map.of(PROPERTY_RELEASE_VERSION, "0")), + Arguments.of(Map.of(PROPERTY_RELEASE_VERSION, "-1")), + Arguments.of(Map.of(PROPERTY_RELEASE_VERSION, "11.0.1")), + Arguments.of(Map.of(PROPERTY_RELEASE_VERSION, new ArrayList())), + Arguments.of(Map.of(PROPERTY_RELEASE_VERSION, Integer.valueOf(0))), + Arguments.of(Map.of(PROPERTY_RELEASE_VERSION, Integer.valueOf(-1))) + ); } // Not the best test but all I can do since ZipFileSystem @@ -148,16 +155,16 @@ public class MultiReleaseJarTest { Map env = new HashMap<>(); // no configuration, treat multi-release jar as unversioned try (FileSystem fs = FileSystems.newFileSystem(mruri, env)) { - Assert.assertTrue(readAndCompare(fs, 8)); + assertTrue(readAndCompare(fs, 8)); } env.put(PROPERTY_RELEASE_VERSION, "runtime"); // a configuration and jar file is multi-release try (FileSystem fs = FileSystems.newFileSystem(mruri, env)) { - Assert.assertTrue(readAndCompare(fs, MAJOR_VERSION)); + assertTrue(readAndCompare(fs, MAJOR_VERSION)); } // a configuration but jar file is unversioned try (FileSystem fs = FileSystems.newFileSystem(uvuri, env)) { - Assert.assertTrue(readAndCompare(fs, 8)); + assertTrue(readAndCompare(fs, 8)); } } @@ -167,7 +174,8 @@ public class MultiReleaseJarTest { return src.contains("return " + expected); } - @Test(dataProvider="strings") + @ParameterizedTest + @MethodSource("createStrings") public void testStrings(String value, int expected, String ignorable) throws Throwable { stringEnv.clear(); stringEnv.put(PROPERTY_RELEASE_VERSION, value); @@ -176,7 +184,8 @@ public class MultiReleaseJarTest { runTest(stringEnv, expected); } - @Test(dataProvider="integers") + @ParameterizedTest + @MethodSource("createIntegers") public void testIntegers(Integer value, int expected, Integer ignorable) throws Throwable { integerEnv.clear(); integerEnv.put(PROPERTY_RELEASE_VERSION, value); @@ -185,7 +194,8 @@ public class MultiReleaseJarTest { runTest(integerEnv, expected); } - @Test(dataProvider="versions") + @ParameterizedTest + @MethodSource("createVersions") public void testVersions(Version value, int expected, Version ignorable) throws Throwable { versionEnv.clear(); versionEnv.put(PROPERTY_RELEASE_VERSION, value); @@ -210,30 +220,34 @@ public class MultiReleaseJarTest { * @throws Throwable Exception thrown for anything other than the expected * IllegalArgumentException */ - @Test(dataProvider="invalidVersions") + @ParameterizedTest + @MethodSource("invalidVersions") public void testInvalidVersions(Map env) throws Throwable { - Assert.assertThrows(IllegalArgumentException.class, () -> + assertThrows(IllegalArgumentException.class, () -> FileSystems.newFileSystem(Path.of(userdir, "multi-release.jar"), env)); } // The following tests are for backwards compatibility to validate that // the original property still works - @Test(dataProvider="strings") + @ParameterizedTest + @MethodSource("createStrings") public void testMRStrings(String value, int expected, String ignorable) throws Throwable { stringEnv.clear(); stringEnv.put(PROPERTY_MULTI_RELEASE, value); runTest(stringEnv, expected); } - @Test(dataProvider="integers") + @ParameterizedTest + @MethodSource("createIntegers") public void testMRIntegers(Integer value, int expected, Integer ignorable) throws Throwable { integerEnv.clear(); integerEnv.put(PROPERTY_MULTI_RELEASE, value); runTest(integerEnv, expected); } - @Test(dataProvider="versions") + @ParameterizedTest + @MethodSource("createVersions") public void testMRVersions(Version value, int expected, Version ignorable) throws Throwable { versionEnv.clear(); versionEnv.put(PROPERTY_MULTI_RELEASE, value); @@ -250,7 +264,7 @@ public class MultiReleaseJarTest { byte [] bytes = Files.readAllBytes(version); Class vcls = (new ByteArrayClassLoader(fs)).defineClass(className, bytes); MethodHandle mh = MethodHandles.lookup().findVirtual(vcls, "getVersion", mt); - Assert.assertEquals((int)mh.invoke(vcls.getDeclaredConstructor().newInstance()), expected); + assertEquals(expected, (int)mh.invoke(vcls.getDeclaredConstructor().newInstance())); } } @@ -281,7 +295,7 @@ public class MultiReleaseJarTest { jb.build(); Map env = Map.of(PROPERTY_RELEASE_VERSION, "runtime"); try (FileSystem fs = FileSystems.newFileSystem(uri, env)) { - Assert.assertTrue(true); + assertTrue(true); } Files.delete(jfpath); } @@ -300,9 +314,9 @@ public class MultiReleaseJarTest { URI customJar = new URI("jar", ssp , null); try (FileSystem fs = FileSystems.newFileSystem(customJar, env)) { if (expected) { - Assert.assertTrue(readAndCompare(fs, MAJOR_VERSION)); + assertTrue(readAndCompare(fs, MAJOR_VERSION)); } else { - Assert.assertTrue(readAndCompare(fs, 8)); + assertTrue(readAndCompare(fs, 8)); } } Files.delete(filePath); diff --git a/test/jdk/jdk/nio/zipfs/testng/TEST.properties b/test/jdk/jdk/nio/zipfs/testng/TEST.properties deleted file mode 100644 index f069323c2f9..00000000000 --- a/test/jdk/jdk/nio/zipfs/testng/TEST.properties +++ /dev/null @@ -1,4 +0,0 @@ -# Zip FS unit tests uses TestNG -modules = jdk.zipfs -TestNG.dirs = . - diff --git a/test/jdk/jdk/nio/zipfs/testng/util/ZipFsBaseTest.java b/test/jdk/jdk/nio/zipfs/util/ZipFsBaseTest.java similarity index 80% rename from test/jdk/jdk/nio/zipfs/testng/util/ZipFsBaseTest.java rename to test/jdk/jdk/nio/zipfs/util/ZipFsBaseTest.java index 6686ff0e920..1e6e1a385b1 100644 --- a/test/jdk/jdk/nio/zipfs/testng/util/ZipFsBaseTest.java +++ b/test/jdk/jdk/nio/zipfs/util/ZipFsBaseTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -23,7 +23,7 @@ */ package util; -import org.testng.annotations.DataProvider; +import org.junit.jupiter.params.provider.Arguments; import java.io.File; import java.io.IOException; @@ -34,7 +34,6 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.security.SecureRandom; -import java.util.Arrays; import java.util.Comparator; import java.util.Map; import java.util.stream.Stream; @@ -43,7 +42,9 @@ import java.util.zip.ZipFile; import static java.lang.String.format; import static java.util.stream.Collectors.joining; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; public class ZipFsBaseTest { @@ -53,49 +54,46 @@ public class ZipFsBaseTest { private static final SecureRandom random = new SecureRandom(); /** - * DataProvider used to specify the Zip FS properties to use when creating + * MethodSource used to specify the Zip FS properties to use when creating * the Zip File along with the compression method used * * @return Zip FS properties and compression method used by the tests */ - @DataProvider(name = "zipfsMap") - protected Object[][] zipfsMap() { - return new Object[][]{ - {Map.of("create", "true"), ZipEntry.DEFLATED}, - {Map.of("create", "true", "noCompression", "true"), - ZipEntry.STORED}, - {Map.of("create", "true", "noCompression", "false"), - ZipEntry.DEFLATED} - }; + protected static Stream zipfsMap() { + return Stream.of( + Arguments.of(Map.of("create", "true"), ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "noCompression", "true"), + ZipEntry.STORED), + Arguments.of(Map.of("create", "true", "noCompression", "false"), + ZipEntry.DEFLATED) + ); } /* - * DataProvider used to verify that an entry can be copied or moved within + * MethodSource used to verify that an entry can be copied or moved within * a Zip file system using a different compression from when the entry * was first created */ - @DataProvider(name = "copyMoveMap") - protected Object[][] copyMoveMap() { - return new Object[][]{ - {Map.of("create", "true"), ZipEntry.DEFLATED, ZipEntry.STORED}, - {Map.of("create", "true", "noCompression", "true"), - ZipEntry.STORED, ZipEntry.DEFLATED}, - {Map.of("create", "true", "noCompression", "false"), - ZipEntry.DEFLATED, ZipEntry.STORED} - }; + protected static Stream copyMoveMap() { + return Stream.of( + Arguments.of(Map.of("create", "true"), ZipEntry.DEFLATED, ZipEntry.STORED), + Arguments.of(Map.of("create", "true", "noCompression", "true"), + ZipEntry.STORED, ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "noCompression", "false"), + ZipEntry.DEFLATED, ZipEntry.STORED) + ); } /** - * DataProvider with the compression methods to be used for a given test run + * MethodSource with the compression methods to be used for a given test run * * @return Compression methods to test with */ - @DataProvider(name = "compressionMethods") - protected Object[][] compressionMethods() { - return new Object[][]{ - {ZipEntry.DEFLATED}, - {ZipEntry.STORED} - }; + protected static Stream compressionMethods() { + return Stream.of( + Arguments.of(ZipEntry.DEFLATED), + Arguments.of(ZipEntry.STORED) + ); } /** @@ -137,7 +135,7 @@ public class ZipFsBaseTest { // check entries with Zip API try (ZipFile zf = new ZipFile(zipfile.toFile())) { // check entry count - assertEquals(entries.length, zf.size()); + assertEquals(zf.size(), entries.length); // Check compression method and content of each entry for (Entry e : entries) { @@ -147,7 +145,7 @@ public class ZipFsBaseTest { System.out.printf("Entry Name: %s, method: %s, Expected Method: %s%n", e.name, ze.getMethod(), e.method); } - assertEquals(e.method, ze.getMethod()); + assertEquals(ze.getMethod(), e.method); try (InputStream in = zf.getInputStream(ze)) { byte[] bytes = in.readAllBytes(); if (DEBUG) { @@ -155,7 +153,7 @@ public class ZipFsBaseTest { new String(bytes), new String(e.bytes)); } - assertTrue(Arrays.equals(bytes, e.bytes)); + assertArrayEquals(bytes, e.bytes); } } } @@ -166,7 +164,7 @@ public class ZipFsBaseTest { Path top = fs.getPath("/"); long count = Files.find(top, Integer.MAX_VALUE, (path, attrs) -> attrs.isRegularFile()).count(); - assertEquals(entries.length, count); + assertEquals(count, entries.length); // Check content of each entry for (Entry e : entries) { @@ -175,7 +173,7 @@ public class ZipFsBaseTest { System.out.printf("Entry name = %s, bytes= %s, actual=%s%n", e.name, new String(Files.readAllBytes(file)), new String(e.bytes)); } - assertEquals(Files.readAllBytes(file), e.bytes); + assertArrayEquals(e.bytes, Files.readAllBytes(file)); } } } @@ -189,7 +187,7 @@ public class ZipFsBaseTest { * @param source The path of the file to add to the Zip File * @throws IOException If an error occurs while creating/updating the Zip file */ - protected void zip(Path zipFile, Map env, Path source) throws IOException { + protected static void zip(Path zipFile, Map env, Path source) throws IOException { if (DEBUG) { System.out.printf("File:%s, adding:%s%n", zipFile.toAbsolutePath(), source); } @@ -208,7 +206,7 @@ public class ZipFsBaseTest { * @param entries The entries to add to the Zip File * @throws IOException If an error occurs while creating the Zip file */ - protected void zip(Path zipFile, Map env, + protected static void zip(Path zipFile, Map env, Entry... entries) throws IOException { if (DEBUG) { System.out.printf("Creating file: %s, env: %s%n", zipFile, formatMap(env)); diff --git a/test/jdk/sun/java2d/OpenGL/FlipCoexistTest.java b/test/jdk/sun/java2d/OpenGL/FlipCoexistTest.java new file mode 100644 index 00000000000..beb5da887b4 --- /dev/null +++ b/test/jdk/sun/java2d/OpenGL/FlipCoexistTest.java @@ -0,0 +1,134 @@ +/* + * Copyright Amazon.com Inc. 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.awt.Color; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferStrategy; +import java.awt.image.BufferedImage; +import java.io.File; + +import javax.imageio.ImageIO; + +/** + * @test + * @bug 8378201 + * @key headful + * @summary Verifies that WINDOW and FLIP_BACKBUFFER surfaces sharing the same X + * Window render and flip correctly + * @run main/othervm FlipCoexistTest + * @run main/othervm -Dsun.java2d.opengl=True FlipCoexistTest + */ +public final class FlipCoexistTest { + + private static final int SIZE = 200; + private static final int TOLERANCE = 10; + + public static void main(String[] args) throws Exception { + Frame f = new Frame("FlipCoexistTest"); + try { + f.setUndecorated(true); + f.setSize(SIZE, SIZE); + f.setLocation(100, 100); + f.setVisible(true); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(1000); + + int w = f.getWidth(); + int h = f.getHeight(); + + // Fill window RED via direct render (WINDOW surface) + Graphics g = f.getGraphics(); + g.setColor(Color.RED); + g.fillRect(0, 0, w, h); + g.dispose(); + robot.waitForIdle(); + robot.delay(500); + + // Request flip if available, blit is also useful to cover + f.createBufferStrategy(2); + BufferStrategy bs = f.getBufferStrategy(); + + // Render BLUE to back buffer, do not flip yet + Graphics bg = bs.getDrawGraphics(); + bg.setColor(Color.BLUE); + bg.fillRect(0, 0, w, h); + bg.dispose(); + + // Paint small GREEN rect via direct render + g = f.getGraphics(); + g.setColor(Color.GREEN); + g.fillRect(0, 0, 10, 10); + g.dispose(); + robot.waitForIdle(); + robot.delay(500); + + // GREEN rect must be visible + check(robot, f, 5, 5, Color.GREEN, "small rect"); + + // RED must survive the context round-trip + check(robot, f, w / 2, h / 2, Color.RED, "survived"); + + // Show back buffer, BLUE must appear + bs.show(); + + robot.waitForIdle(); + robot.delay(500); + check(robot, f, w / 2, h / 2, Color.BLUE, "flip"); + } finally { + f.dispose(); + } + } + + private static void check(Robot robot, Frame frame, int x, int y, Color exp, + String desc) + { + Point loc = frame.getLocationOnScreen(); + Color c = robot.getPixelColor(loc.x + x, loc.y + y); + if (!isAlmostEqual(c, exp)) { + saveImage(robot, frame, desc); + throw new RuntimeException("%s: %s != %s".formatted(desc, exp, c)); + } + } + + private static void saveImage(Robot r, Frame f, String name) { + try { + Rectangle rect = f.getBounds(); + BufferedImage img = r.createScreenCapture(rect); + ImageIO.write(img, "png", new File(name + ".png")); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static boolean isAlmostEqual(Color c1, Color c2) { + return Math.abs(c1.getRed() - c2.getRed()) <= TOLERANCE + && Math.abs(c1.getGreen() - c2.getGreen()) <= TOLERANCE + && Math.abs(c1.getBlue() - c2.getBlue()) <= TOLERANCE; + } +} diff --git a/test/jdk/sun/java2d/OpenGL/MultiWindowFillTest.java b/test/jdk/sun/java2d/OpenGL/MultiWindowFillTest.java new file mode 100644 index 00000000000..59c58d944d7 --- /dev/null +++ b/test/jdk/sun/java2d/OpenGL/MultiWindowFillTest.java @@ -0,0 +1,122 @@ +/* + * Copyright Amazon.com Inc. 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.awt.Color; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import java.io.File; + +import javax.imageio.ImageIO; + +/** + * @test + * @bug 8378201 + * @key headful + * @summary Verifies that window content survives a GL context switch to another + * window and back + * @run main/othervm MultiWindowFillTest + * @run main/othervm -Dsun.java2d.opengl=True MultiWindowFillTest + */ +public final class MultiWindowFillTest { + + private static final int SIZE = 100; + private static final int TOLERANCE = 10; + + public static void main(String[] args) throws Exception { + Frame f1 = new Frame("f1"); + Frame f2 = new Frame("f2"); + try { + f1.setUndecorated(true); + f1.setSize(SIZE, SIZE); + f1.setLocation(100, 100); + f2.setUndecorated(true); + f2.setSize(SIZE, SIZE); + f2.setLocation(300, 100); + + f1.setVisible(true); + f2.setVisible(true); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(1000); + + int w = f1.getWidth(); + int h = f1.getHeight(); + + // Fill both, initializes surfaces + fill(f1, Color.RED, w, h); + fill(f2, Color.BLUE, w, h); + + // Touch both again + fill(f1, Color.RED, 2, 2); + fill(f2, Color.BLUE, 2, 2); + + robot.waitForIdle(); + robot.delay(1000); + + check(robot, f1, w, h, Color.RED, "f1 red"); + check(robot, f2, w, h, Color.BLUE, "f2 blue"); + } finally { + f1.dispose(); + f2.dispose(); + } + } + + private static void fill(Frame frame, Color c, int w, int h) { + Graphics g = frame.getGraphics(); + g.setColor(c); + g.fillRect(0, 0, w, h); + g.dispose(); + } + + private static void check(Robot robot, Frame frame, int w, int h, + Color exp, String desc) + { + Point loc = frame.getLocationOnScreen(); + Color c = robot.getPixelColor(loc.x + w / 2, loc.y + h / 2); + if (!isAlmostEqual(c, exp)) { + saveImage(robot, frame, desc); + throw new RuntimeException("%s: %s != %s".formatted(desc, exp, c)); + } + } + + private static void saveImage(Robot r, Frame f, String name) { + try { + Rectangle rect = f.getBounds(); + BufferedImage img = r.createScreenCapture(rect); + ImageIO.write(img, "png", new File(name + ".png")); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static boolean isAlmostEqual(Color c1, Color c2) { + return Math.abs(c1.getRed() - c2.getRed()) <= TOLERANCE + && Math.abs(c1.getGreen() - c2.getGreen()) <= TOLERANCE + && Math.abs(c1.getBlue() - c2.getBlue()) <= TOLERANCE; + } +} diff --git a/test/jdk/sun/nio/cs/StreamEncoderOut.java b/test/jdk/sun/nio/cs/StreamEncoderOut.java index 90a22512311..90b4aee838d 100644 --- a/test/jdk/sun/nio/cs/StreamEncoderOut.java +++ b/test/jdk/sun/nio/cs/StreamEncoderOut.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -24,10 +24,8 @@ /* @test @bug 8030179 @summary test if the charset encoder deails with surrogate correctly - * @run testng/othervm -esa StreamEncoderOut + * @run junit/othervm -esa StreamEncoderOut */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; import java.io.OutputStream; @@ -35,9 +33,12 @@ import java.io.OutputStreamWriter; import java.nio.charset.Charset; import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import static java.util.stream.Collectors.joining; -@Test public class StreamEncoderOut { enum Input { @@ -57,14 +58,11 @@ public class StreamEncoderOut { } } - @DataProvider(name = "CharsetAndString") - // [Charset, Input] - public static Object[][] makeStreamTestData() { + public static Stream makeStreamTestData() { // Cross product of supported charsets and inputs - return Charset.availableCharsets().values().stream(). - filter(Charset::canEncode). - flatMap(cs -> Stream.of(Input.values()).map(i -> new Object[]{cs, i})). - toArray(Object[][]::new); + return Charset.availableCharsets().values().stream() + .filter(Charset::canEncode) + .flatMap(cs -> Stream.of(Input.values()).map(i -> Arguments.of(cs, i))); } private static String generate(String s, int n) { @@ -79,7 +77,8 @@ public class StreamEncoderOut { public void write(int b) throws IOException {} }; - @Test(dataProvider = "CharsetAndString") + @ParameterizedTest + @MethodSource("makeStreamTestData") public void test(Charset cs, Input input) throws IOException { OutputStreamWriter w = new OutputStreamWriter(DEV_NULL, cs); String t = generate(input.value, 8193); diff --git a/test/jdk/sun/nio/cs/TestUnicodeReversedBOM.java b/test/jdk/sun/nio/cs/TestUnicodeReversedBOM.java index d76bf2f6034..ca0798c13e5 100644 --- a/test/jdk/sun/nio/cs/TestUnicodeReversedBOM.java +++ b/test/jdk/sun/nio/cs/TestUnicodeReversedBOM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -26,16 +26,18 @@ * @bug 8216140 * @summary Test reversed BOM (U+FFFE) in the middle of a byte buffer * passes through during decoding with UnicodeDecoder. - * @run testng TestUnicodeReversedBOM + * @run junit TestUnicodeReversedBOM */ + import java.nio.charset.*; import java.nio.*; import java.util.*; +import java.util.stream.Stream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -@Test public class TestUnicodeReversedBOM { private static byte[] A_REVERSED_BE = {(byte)0x0, (byte)'A', (byte)0xff, (byte)0xfe}; @@ -46,26 +48,25 @@ public class TestUnicodeReversedBOM { private static byte[] BOM_REVERSED_LE = {(byte)0xff, (byte)0xfe, (byte)0xfe, (byte)0xff}; - @DataProvider - // [(byte[])byte array, (Charset)cs] - public static Object[][] ReversedBOM() { - return new Object[][] { - {A_REVERSED_BE, StandardCharsets.UTF_16}, - {A_REVERSED_LE, StandardCharsets.UTF_16}, - {A_REVERSED_BE, StandardCharsets.UTF_16BE}, - {A_REVERSED_LE, StandardCharsets.UTF_16BE}, - {A_REVERSED_BE, StandardCharsets.UTF_16LE}, - {A_REVERSED_LE, StandardCharsets.UTF_16LE}, - {BOM_REVERSED_BE, StandardCharsets.UTF_16}, - {BOM_REVERSED_LE, StandardCharsets.UTF_16}, - {BOM_REVERSED_BE, StandardCharsets.UTF_16BE}, - {BOM_REVERSED_LE, StandardCharsets.UTF_16BE}, - {BOM_REVERSED_BE, StandardCharsets.UTF_16LE}, - {BOM_REVERSED_LE, StandardCharsets.UTF_16LE}, - }; + public static Stream ReversedBOM() { + return Stream.of( + Arguments.of(A_REVERSED_BE, StandardCharsets.UTF_16), + Arguments.of(A_REVERSED_LE, StandardCharsets.UTF_16), + Arguments.of(A_REVERSED_BE, StandardCharsets.UTF_16BE), + Arguments.of(A_REVERSED_LE, StandardCharsets.UTF_16BE), + Arguments.of(A_REVERSED_BE, StandardCharsets.UTF_16LE), + Arguments.of(A_REVERSED_LE, StandardCharsets.UTF_16LE), + Arguments.of(BOM_REVERSED_BE, StandardCharsets.UTF_16), + Arguments.of(BOM_REVERSED_LE, StandardCharsets.UTF_16), + Arguments.of(BOM_REVERSED_BE, StandardCharsets.UTF_16BE), + Arguments.of(BOM_REVERSED_LE, StandardCharsets.UTF_16BE), + Arguments.of(BOM_REVERSED_BE, StandardCharsets.UTF_16LE), + Arguments.of(BOM_REVERSED_LE, StandardCharsets.UTF_16LE) + ); } - @Test(dataProvider = "ReversedBOM") + @ParameterizedTest + @MethodSource("ReversedBOM") public void testReversedBOM(byte[] ba, Charset cs) throws CharacterCodingException { cs.newDecoder() .onMalformedInput(CodingErrorAction.REPORT) diff --git a/test/jdk/sun/security/ec/xec/TestXDH.java b/test/jdk/sun/security/ec/xec/TestXDH.java index a32da875fa5..30c3b433406 100644 --- a/test/jdk/sun/security/ec/xec/TestXDH.java +++ b/test/jdk/sun/security/ec/xec/TestXDH.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -23,20 +23,32 @@ /* * @test - * @bug 8171277 8206915 + * @bug 8171277 8206915 8368841 * @summary Test XDH key agreement * @library /test/lib - * @build jdk.test.lib.Convert * @run main TestXDH */ -import java.security.*; -import java.security.spec.*; -import javax.crypto.*; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PublicKey; +import java.security.PrivateKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.NamedParameterSpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.security.spec.XECPublicKeySpec; +import java.security.spec.XECPrivateKeySpec; +import javax.crypto.KeyAgreement; import java.util.Arrays; import java.util.HexFormat; -import jdk.test.lib.Convert; import jdk.test.lib.hexdump.ASN1Formatter; import jdk.test.lib.hexdump.HexPrinter; @@ -354,7 +366,6 @@ public class TestXDH { throw new RuntimeException("fail: expected=" + result + ", actual=" + HexFormat.of().withUpperCase().formatHex(sharedSecret)); } - } private static void runDiffieHellmanTest(String curveName, String a_pri, @@ -365,9 +376,8 @@ public class TestXDH { KeySpec privateSpec = new XECPrivateKeySpec(paramSpec, HexFormat.of().parseHex(a_pri)); PrivateKey privateKey = kf.generatePrivate(privateSpec); - boolean clearHighBit = curveName.equals("X25519"); KeySpec publicSpec = new XECPublicKeySpec(paramSpec, - Convert.hexStringToBigInteger(clearHighBit, b_pub)); + hexStringToBigInteger(b_pub)); PublicKey publicKey = kf.generatePublic(publicSpec); byte[] encodedPrivateKey = privateKey.getEncoded(); @@ -394,6 +404,21 @@ public class TestXDH { } } + /* + * Converts a hexidecimal string to the corresponding little-endian + * number as a BigInteger + */ + private static BigInteger hexStringToBigInteger(String str) { + BigInteger result = BigInteger.ZERO; + for (int i = 0; i < str.length() / 2; i++) { + int curVal = Character.digit(str.charAt(2 * i), 16); + curVal <<= 4; + curVal += Character.digit(str.charAt(2 * i + 1), 16); + result = result.add(BigInteger.valueOf(curVal).shiftLeft(8 * i)); + } + return result; + } + /* * Ensure that SunEC rejects parameters/points for the wrong curve * when the algorithm ID for a specific curve is specified. diff --git a/test/jdk/sun/security/ec/xec/TestXECOps.java b/test/jdk/sun/security/ec/xec/TestXECOps.java index cdbcf95fc0b..a7533b89c02 100644 --- a/test/jdk/sun/security/ec/xec/TestXECOps.java +++ b/test/jdk/sun/security/ec/xec/TestXECOps.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -31,8 +31,9 @@ * @run main TestXECOps */ -import sun.security.ec.*; - +import sun.security.ec.XECOperations; +import sun.security.ec.XECParameters; +import java.math.BigInteger; import java.security.spec.NamedParameterSpec; import java.util.*; import jdk.test.lib.Convert; @@ -93,6 +94,7 @@ public class TestXECOps { XECParameters.get(RuntimeException::new, paramSpec); XECOperations ops = new XECOperations(settings); + // Test encodedPointMultiply(byte[] k, byte[] u) byte[] basePoint = Convert.byteToByteArray(settings.getBasePoint(), settings.getBytes()); byte[] a = HexFormat.of().parseHex(a_str); @@ -101,18 +103,29 @@ public class TestXECOps { byte[] a_copy = Arrays.copyOf(a, a.length); byte[] b_copy = Arrays.copyOf(b, b.length); - byte[] basePoint_copy = Arrays.copyOf(basePoint, basePoint.length); byte[] resultA = ops.encodedPointMultiply(b, ops.encodedPointMultiply(a, basePoint)); byte[] resultB = ops.encodedPointMultiply(a_copy, - ops.encodedPointMultiply(b_copy, basePoint_copy)); + ops.encodedPointMultiply(b_copy, basePoint)); if (!Arrays.equals(resultA, expectedResult)) { throw new RuntimeException("fail"); } if (!Arrays.equals(resultB, expectedResult)) { throw new RuntimeException("fail"); } + + // Test encodedPointMultiply(byte[] k, BigInteger u) + reverse(basePoint); + BigInteger bp = new BigInteger(1, basePoint); + byte[] c = HexFormat.of().parseHex(a_str); + byte[] d = HexFormat.of().parseHex(b_str); + + byte[] res0 = ops.encodedPointMultiply(d, + ops.encodedPointMultiply(c, bp)); + if (!Arrays.equals(res0, expectedResult)) { + throw new RuntimeException("bigint fail"); + } } private void runTest(String opName, String k_in_str, @@ -125,12 +138,31 @@ public class TestXECOps { NamedParameterSpec paramSpec = new NamedParameterSpec(opName); XECParameters settings = XECParameters.get(RuntimeException::new, paramSpec); - XECOperations ops = new XECOperations(settings); - byte[] u_out = ops.encodedPointMultiply(k_in, u_in); - if (!Arrays.equals(u_out, u_out_expected)) { + // Test encodedPointMultiply(byte[] k, byte[] u) + XECOperations ops = new XECOperations(settings); + byte[] res0 = ops.encodedPointMultiply(k_in, u_in); + + if (!Arrays.equals(res0, u_out_expected)) { throw new RuntimeException("fail"); } + + // Test encodedPointMultiply(byte[] k, BigInteger u) + reverse(u_in); + BigInteger u = new BigInteger(1, u_in); + byte[] res1 = ops.encodedPointMultiply(k_in, u); + + if (!Arrays.equals(res1, u_out_expected)) { + throw new RuntimeException("fail"); + } + } + + private static void reverse(byte[] array) { + for (int i = 0; i < array.length / 2; i++) { + byte temp = array[i]; + array[i] = array[array.length - i - 1]; + array[array.length - i - 1] = temp; + } } } diff --git a/test/jdk/sun/security/ssl/HybridKeyExchange/TestHybrid.java b/test/jdk/sun/security/ssl/HybridKeyExchange/TestHybrid.java new file mode 100644 index 00000000000..82314c31dcf --- /dev/null +++ b/test/jdk/sun/security/ssl/HybridKeyExchange/TestHybrid.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2026, 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 8379433 + * @summary Test expected DecapsulateException thrown by Hybrid KEM implementation + * @modules java.base/sun.security.ssl + * @run main/othervm TestHybrid +*/ +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Provider; +import java.util.Arrays; +import javax.crypto.DecapsulateException; +import javax.crypto.KEM; + +public class TestHybrid { + + public static void main(String[] args) throws Exception { + + Class clazz = Class.forName("sun.security.ssl.HybridProvider"); + Provider p = (Provider) clazz.getField("PROVIDER").get(null); + + String alg = "X25519MLKEM768"; + KeyPairGenerator kpg = KeyPairGenerator.getInstance(alg, p); + KeyPair kp = kpg.generateKeyPair(); + + KEM kem = KEM.getInstance(alg, p); + KEM.Encapsulator e = kem.newEncapsulator(kp.getPublic()); + KEM.Decapsulator d = kem.newDecapsulator(kp.getPrivate()); + + int secretSize = e.secretSize(); + KEM.Encapsulated enc = e.encapsulate(); + byte[] ciphertext = enc.encapsulation(); + + byte[] badCiphertext = Arrays.copyOf(ciphertext, + ciphertext.length - 1); + try { + d.decapsulate(badCiphertext, 0, secretSize, "Generic"); + throw new RuntimeException( + "Expected DecapsulateException not thrown"); + } catch (DecapsulateException expected) { + System.out.println("Expected DecapsulateException thrown"); + } + } +} diff --git a/test/jdk/sun/security/util/HexDumpEncoderTests.java b/test/jdk/sun/security/util/HexDumpEncoderTests.java index 9ff08cdb192..53f9001ae7a 100644 --- a/test/jdk/sun/security/util/HexDumpEncoderTests.java +++ b/test/jdk/sun/security/util/HexDumpEncoderTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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,6 +27,10 @@ import sun.security.util.HexDumpEncoder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; import java.util.Arrays; /* @@ -38,7 +42,6 @@ import java.util.Arrays; */ public class HexDumpEncoderTests { - private static String[] getTestCommand(final String encoding) { return new String[]{ "--add-modules", "java.base", @@ -54,24 +57,34 @@ public class HexDumpEncoderTests { final var resultIso = ProcessTools.executeTestJava(testCommandIso); resultIso.shouldHaveExitValue(0); + final String latin1ResultFromFile = Files.readString( + Path.of("ISO-8859-1.txt")); - // This will take all available StandardCharsets and test them all comparing to the ISO_8859_1 - // Dome im parallel, as this is significantly faster + // This will take all available StandardCharsets and test them all + // comparing to the ISO_8859_1. + // Done im parallel, as this is significantly faster Arrays.stream(StandardCharsets.class.getDeclaredFields()) .parallel() .forEach(field -> { - if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) { + if (java.lang.reflect.Modifier + .isStatic(field.getModifiers())) { try { - final var charset = (Charset) field.get(StandardCharsets.ISO_8859_1); // getting the charset to test + // getting the charset to test + final var charset = (Charset) field.get(null); - final var testCommand = getTestCommand(charset.name()); + final var testCommand = + getTestCommand(charset.name()); - final var result = ProcessTools.executeTestJava(testCommand); + final var result = + ProcessTools.executeTestJava(testCommand); result.shouldHaveExitValue(0); + final String resultFromFile = Files.readString( + Path.of(charset.name()+".txt")); - // The outputs of the ISO encoding must be identical to the one tested - Asserts.assertEquals(resultIso.getStdout(), - result.getStdout(), + // The outputs of the ISO encoding must be identical + // to the one tested + Asserts.assertEquals(latin1ResultFromFile, + resultFromFile, "Encoding " + charset.name()); } catch (Exception e) { throw new RuntimeException(e); @@ -86,14 +99,26 @@ public class HexDumpEncoderTests { * This will test the encode and encode buffer functions at once, * as they are both representing the string in LATIN_1 *

- * The output is put as a system.out + * The output is put to the file in scratch dir with corresponding + * encoding name */ public static void main(String[] args) throws Exception { final var encoder = new HexDumpEncoder(); - System.out.printf("\nCert Encoded With Encode Buffer: %s\n", encoder.encodeBuffer(new byte[100])); - System.out.printf("\nCert Encoded With Encode: %s\n", encoder.encode(new byte[100])); + final String encodeBufferResult = + encoder.encodeBuffer(new byte[100]); + final String encodeResult = encoder.encode(new byte[100]); + + final String content = String.format(""" + Cert Encoded With Encode Buffer: %s + Cert Encoded With Encode: %s""", + encodeBufferResult, encodeResult); + Files.writeString( + Paths.get(Charset.defaultCharset().displayName() + ".txt"), + content, + StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING); } } } diff --git a/test/jdk/sun/security/util/math/TestIntegerModuloP.java b/test/jdk/sun/security/util/math/TestIntegerModuloP.java index e1c02365424..f2b73223efc 100644 --- a/test/jdk/sun/security/util/math/TestIntegerModuloP.java +++ b/test/jdk/sun/security/util/math/TestIntegerModuloP.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ * @summary Test proper operation of integer field arithmetic * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly * @build BigIntegerModuloP - * @run main TestIntegerModuloP sun.security.util.math.intpoly.IntegerPolynomial25519 32 0 + * @run main TestIntegerModuloP sun.security.util.math.intpoly.IntegerPolynomial25519 31 0 */ /* diff --git a/test/jdk/sun/text/IntHashtable/Bug4705389.java b/test/jdk/sun/text/IntHashtable/Bug4705389.java index bb688439706..c74dd03b532 100644 --- a/test/jdk/sun/text/IntHashtable/Bug4705389.java +++ b/test/jdk/sun/text/IntHashtable/Bug4705389.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, 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 @@ -26,7 +26,6 @@ * @bug 4705389 * @summary Make sure to find removed slots, which test case will be timed out without the fix. * @modules java.base/sun.text - * @run main/timeout=10 Bug4705389 */ import sun.text.IntHashtable; diff --git a/test/jdk/sun/tools/jcmd/TestJcmdSanity.java b/test/jdk/sun/tools/jcmd/TestJcmdSanity.java index 92f73597ce6..559b6b7d5a4 100644 --- a/test/jdk/sun/tools/jcmd/TestJcmdSanity.java +++ b/test/jdk/sun/tools/jcmd/TestJcmdSanity.java @@ -33,7 +33,6 @@ import java.util.List; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.Platform; -import jdk.test.lib.Utils; /* * @test @@ -182,5 +181,10 @@ public class TestJcmdSanity { output.shouldNotContain("*** Handler was modified!"); output.shouldNotContain("*** Expected: "); // e.g. *** Expected: javaSignalHandler in ... } + + // Should find file descriptor counting on Mac and Linux + if (Platform.isLinux() || Platform.isOSX()) { + output.shouldMatch("Open File Descriptors: \\d+"); + } } } diff --git a/test/jdk/sun/util/resources/TimeZone/Bug8139107.java b/test/jdk/sun/util/resources/TimeZone/Bug8139107.java index e00d4c88889..575dd7c291e 100644 --- a/test/jdk/sun/util/resources/TimeZone/Bug8139107.java +++ b/test/jdk/sun/util/resources/TimeZone/Bug8139107.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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,12 +27,13 @@ * @summary Test that date parsing with DateTimeFormatter pattern * that contains timezone field doesn't trigger NPE. All supported * locales are tested. - * @run testng/timeout=480 Bug8139107 + * @run junit/timeout=480 Bug8139107 */ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.Locale; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.Test; public class Bug8139107 { diff --git a/test/jdk/sun/util/resources/TimeZone/ChineseTimeZoneNameTest.java b/test/jdk/sun/util/resources/TimeZone/ChineseTimeZoneNameTest.java index 335d147f9ab..258edc38a59 100644 --- a/test/jdk/sun/util/resources/TimeZone/ChineseTimeZoneNameTest.java +++ b/test/jdk/sun/util/resources/TimeZone/ChineseTimeZoneNameTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -26,7 +26,7 @@ * @bug 8275721 8174269 * @modules jdk.localedata * @summary Checks Chinese time zone names for `UTC` using CLDR are consistent - * @run testng ChineseTimeZoneNameTest + * @run junit ChineseTimeZoneNameTest */ import java.time.Instant; @@ -35,11 +35,10 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Locale; -import static org.testng.Assert.assertEquals; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; -@Test public class ChineseTimeZoneNameTest { private static final Locale SIMPLIFIED_CHINESE = Locale.forLanguageTag("zh-Hans"); @@ -47,8 +46,7 @@ public class ChineseTimeZoneNameTest { private static final ZonedDateTime EPOCH_UTC = ZonedDateTime.ofInstant(Instant.ofEpochSecond (0), ZoneId.of ("UTC")); - @DataProvider(name="locales") - Object[][] data() { + private static Object[][] data() { return new Object[][] { {Locale.CHINESE, SIMPLIFIED_CHINESE}, {Locale.SIMPLIFIED_CHINESE, SIMPLIFIED_CHINESE}, @@ -61,11 +59,12 @@ public class ChineseTimeZoneNameTest { }; } - @Test(dataProvider="locales") + @ParameterizedTest + @MethodSource("data") public void test_ChineseTimeZoneNames(Locale testLoc, Locale resourceLoc) { - assertEquals(DateTimeFormatter.ofPattern("z", testLoc).format(EPOCH_UTC), - DateTimeFormatter.ofPattern("z", resourceLoc).format(EPOCH_UTC)); - assertEquals(DateTimeFormatter.ofPattern("zzzz", testLoc).format(EPOCH_UTC), - DateTimeFormatter.ofPattern("zzzz", resourceLoc).format(EPOCH_UTC)); + assertEquals(DateTimeFormatter.ofPattern("z", resourceLoc).format(EPOCH_UTC), + DateTimeFormatter.ofPattern("z", testLoc).format(EPOCH_UTC)); + assertEquals(DateTimeFormatter.ofPattern("zzzz", resourceLoc).format(EPOCH_UTC), + DateTimeFormatter.ofPattern("zzzz", testLoc).format(EPOCH_UTC)); } } diff --git a/test/jdk/sun/util/resources/cldr/Bug8202764.java b/test/jdk/sun/util/resources/cldr/Bug8202764.java index 6f3e40e6201..e7fa110671a 100644 --- a/test/jdk/sun/util/resources/cldr/Bug8202764.java +++ b/test/jdk/sun/util/resources/cldr/Bug8202764.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -28,11 +28,9 @@ * @summary Checks time zone names are consistent with aliased ids, * between DateFormatSymbols.getZoneStrings() and getDisplayName() * of TimeZone/ZoneId classes - * @run testng/othervm Bug8202764 + * @run junit/othervm Bug8202764 */ -import static org.testng.Assert.assertEquals; - import java.time.ZoneId; import java.time.format.TextStyle; import java.text.DateFormatSymbols; @@ -41,7 +39,8 @@ import java.util.Locale; import java.util.Set; import java.util.TimeZone; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; public class Bug8202764 { @@ -53,15 +52,15 @@ public class Bug8202764 { .forEach(zone -> { System.out.println(zone[0]); TimeZone tz = TimeZone.getTimeZone(zone[0]); - assertEquals(zone[1], tz.getDisplayName(false, TimeZone.LONG, Locale.US)); - assertEquals(zone[2], tz.getDisplayName(false, TimeZone.SHORT, Locale.US)); - assertEquals(zone[3], tz.getDisplayName(true, TimeZone.LONG, Locale.US)); - assertEquals(zone[4], tz.getDisplayName(true, TimeZone.SHORT, Locale.US)); + assertEquals(tz.getDisplayName(false, TimeZone.LONG, Locale.US), zone[1]); + assertEquals(tz.getDisplayName(false, TimeZone.SHORT, Locale.US), zone[2]); + assertEquals(tz.getDisplayName(true, TimeZone.LONG, Locale.US), zone[3]); + assertEquals(tz.getDisplayName(true, TimeZone.SHORT, Locale.US), zone[4]); if (zoneIds.contains(zone[0])) { // Some of the ids, e.g. three-letter ids are not supported in ZoneId ZoneId zi = tz.toZoneId(); - assertEquals(zone[5], zi.getDisplayName(TextStyle.FULL, Locale.US)); - assertEquals(zone[6], zi.getDisplayName(TextStyle.SHORT, Locale.US)); + assertEquals(zi.getDisplayName(TextStyle.FULL, Locale.US), zone[5]); + assertEquals(zi.getDisplayName(TextStyle.SHORT, Locale.US), zone[6]); } }); } diff --git a/test/jdk/sun/util/resources/cldr/TimeZoneNamesTest.java b/test/jdk/sun/util/resources/cldr/TimeZoneNamesTest.java index 05b0114f1b4..6d21cd5613a 100644 --- a/test/jdk/sun/util/resources/cldr/TimeZoneNamesTest.java +++ b/test/jdk/sun/util/resources/cldr/TimeZoneNamesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -28,7 +28,7 @@ * @modules jdk.localedata * @summary Checks CLDR time zone names are generated correctly at * either build or runtime - * @run testng TimeZoneNamesTest + * @run junit TimeZoneNamesTest */ import java.text.DateFormatSymbols; @@ -39,16 +39,15 @@ import java.util.Locale; import java.util.Objects; import java.util.TimeZone; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; -@Test public class TimeZoneNamesTest { - @DataProvider - Object[][] sampleTZs() { + private static Object[][] sampleTZs() { return new Object[][] { // tzid, locale, style, expected @@ -219,19 +218,20 @@ public class TimeZoneNamesTest { } - @Test(dataProvider="sampleTZs") + @ParameterizedTest + @MethodSource("sampleTZs") public void test_tzNames(String tzid, Locale locale, String lstd, String sstd, String ldst, String sdst, String lgen, String sgen) { // Standard time - assertEquals(TimeZone.getTimeZone(tzid).getDisplayName(false, TimeZone.LONG, locale), lstd); - assertEquals(TimeZone.getTimeZone(tzid).getDisplayName(false, TimeZone.SHORT, locale), sstd); + assertEquals(lstd, TimeZone.getTimeZone(tzid).getDisplayName(false, TimeZone.LONG, locale)); + assertEquals(sstd, TimeZone.getTimeZone(tzid).getDisplayName(false, TimeZone.SHORT, locale)); // daylight saving time - assertEquals(TimeZone.getTimeZone(tzid).getDisplayName(true, TimeZone.LONG, locale), ldst); - assertEquals(TimeZone.getTimeZone(tzid).getDisplayName(true, TimeZone.SHORT, locale), sdst); + assertEquals(ldst, TimeZone.getTimeZone(tzid).getDisplayName(true, TimeZone.LONG, locale)); + assertEquals(sdst, TimeZone.getTimeZone(tzid).getDisplayName(true, TimeZone.SHORT, locale)); // generic name - assertEquals(ZoneId.of(tzid).getDisplayName(TextStyle.FULL, locale), lgen); - assertEquals(ZoneId.of(tzid).getDisplayName(TextStyle.SHORT, locale), sgen); + assertEquals(lgen, ZoneId.of(tzid).getDisplayName(TextStyle.FULL, locale)); + assertEquals(sgen, ZoneId.of(tzid).getDisplayName(TextStyle.SHORT, locale)); } // Make sure getZoneStrings() returns non-empty string array diff --git a/test/jdk/tools/jpackage/TEST.properties b/test/jdk/tools/jpackage/TEST.properties index 5cc4aa7a1b9..99af197b989 100644 --- a/test/jdk/tools/jpackage/TEST.properties +++ b/test/jdk/tools/jpackage/TEST.properties @@ -8,7 +8,7 @@ requires.properties = \ jpackage.test.SQETest \ jpackage.test.MacSignTests -maxOutputSize = 2000000 +maxOutputSize = 10000000 # Run jpackage tests on windows platform sequentially. # Having "share" directory in the list affects tests on other platforms. diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/CannedArgumentTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/CannedArgumentTest.java new file mode 100644 index 00000000000..8653e9c7508 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/CannedArgumentTest.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2026, 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 jdk.jpackage.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.nio.file.Path; +import jdk.jpackage.test.CannedFormattedStringTest.Formatter; +import org.junit.jupiter.api.Test; + +class CannedArgumentTest { + + @Test + void test_cannedAbsolutePath() { + var a = Formatter.MESSAGE_FORMAT.create("Current directory: {0}", CannedArgument.cannedAbsolutePath("foo")); + assertEquals("Current directory: " + Path.of("foo").toAbsolutePath(), a.getValue()); + assertEquals("Current directory: {0}+[AbsolutePath(foo)]", a.toString()); + } +} diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/CannedFormattedStringTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/CannedFormattedStringTest.java new file mode 100644 index 00000000000..2e4ad9ab754 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/CannedFormattedStringTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2026, 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 jdk.jpackage.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; + +import java.text.MessageFormat; +import java.util.List; +import java.util.function.BiFunction; +import org.junit.jupiter.api.Test; + +class CannedFormattedStringTest { + + @Test + void test_getValue() { + var a = Formatter.MESSAGE_FORMAT.create("Hello {0}! Bye {0}", "Duke"); + assertEquals("Hello Duke! Bye Duke", a.getValue()); + assertEquals("Hello {0}! Bye {0}+[Duke]", a.toString()); + + var b = Formatter.MESSAGE_FORMAT.create("Hello {0}! Bye {0}"); + assertEquals("Hello {0}! Bye {0}", b.getValue()); + assertEquals("Hello {0}! Bye {0}", b.toString()); + } + + @Test + void test_equals() { + var a = Formatter.MESSAGE_FORMAT.create("Hello {0}! Bye {0}", "Duke"); + var b = Formatter.MESSAGE_FORMAT.create("Hello {0}! Bye {0}", "Duke"); + + assertEquals(a, b); + + a = Formatter.MESSAGE_FORMAT.create("Hello {0}! Bye {0}", "Duke"); + b = Formatter.MESSAGE_FORMAT.create("Hello {0}! Bye {0}", "Java"); + + assertNotEquals(a, b); + } + + @Test + void test_addPrefix() { + var a = Formatter.MESSAGE_FORMAT.create("Hello {0}! Bye {0}", "Duke"); + var b = a.addPrefix("{0} and {0}").addPrefix("They say: {0}"); + var str = "Hello Duke! Bye Duke"; + assertEquals(str, a.getValue()); + assertEquals("They say: " + str + " and " + str, b.getValue()); + assertEquals("They say: {0}+[{0} and {0}, Hello {0}! Bye {0}, Duke]", b.toString()); + + var c = a.addPrefix("{0} and {0}").addPrefix("They say: {0}"); + assertEquals(c, b); + assertNotSame(b, c); + } + + @Test + void test_mapArgs() { + var a = Formatter.MESSAGE_FORMAT.create("Hello {0}! Bye {0}", "Duke"); + var b = a.mapArgs(arg -> { + assertEquals("Duke", arg); + return "Java"; + }); + + assertEquals("Hello Duke! Bye Duke", a.getValue()); + assertEquals("Hello Java! Bye Java", b.getValue()); + } + + enum Formatter implements BiFunction { + MESSAGE_FORMAT { + @Override + public String apply(String format, Object[] formatArgs) { + return MessageFormat.format(format, formatArgs); + } + }, + ; + + CannedFormattedString create(String format, Object ... formatArgs) { + return new CannedFormattedString(this, format, List.of(formatArgs)); + } + } +} diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JPackageStringBundleTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JPackageStringBundleTest.java index bf1eb2bdaec..0512d03b76a 100644 --- a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JPackageStringBundleTest.java +++ b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JPackageStringBundleTest.java @@ -24,6 +24,7 @@ package jdk.jpackage.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrowsExactly; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -46,6 +47,24 @@ class JPackageStringBundleTest { assertFalse(JPackageStringBundle.MAIN.cannedFormattedString("error.version-string-empty").getValue().isBlank()); } + @Test + void test_cannedFormattedString_equals() { + var a = JPackageStringBundle.MAIN.cannedFormattedString("error.version-string-empty"); + var b = JPackageStringBundle.MAIN.cannedFormattedString("error.version-string-empty"); + + assertEquals(a, b); + + a = JPackageStringBundle.MAIN.cannedFormattedString("message.error-header", "foo"); + b = JPackageStringBundle.MAIN.cannedFormattedString("message.error-header", "foo"); + + assertEquals(a, b); + + a = JPackageStringBundle.MAIN.cannedFormattedString("message.error-header", "foo"); + b = JPackageStringBundle.MAIN.cannedFormattedString("message.error-header", "bar"); + + assertNotEquals(a, b); + } + @Test void test_cannedFormattedStringAsPattern() { var pred = JPackageStringBundle.MAIN.cannedFormattedStringAsPattern("error.version-string-empty", UNREACHABLE_FORMAT_ARG_MAPPER).asMatchPredicate(); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java index 2dcccc673a9..ce2cb6f6b57 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java @@ -26,6 +26,7 @@ import static java.util.stream.Collectors.toMap; import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; +import static jdk.jpackage.test.JPackageCommand.DEFAULT_VERSION; import java.io.IOException; import java.nio.file.Path; @@ -65,7 +66,7 @@ public record AppImageFile(String mainLauncherName, Optional mainLaunche } public AppImageFile(String mainLauncherName, Optional mainLauncherClassName) { - this(mainLauncherName, mainLauncherClassName, "1.0", false, Map.of(mainLauncherName, Map.of())); + this(mainLauncherName, mainLauncherClassName, DEFAULT_VERSION, false, Map.of(mainLauncherName, Map.of())); } public AppImageFile(String mainLauncherName, String mainLauncherClassName) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedArgument.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedArgument.java index b50d12ac4ec..346b172fa35 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedArgument.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedArgument.java @@ -22,7 +22,37 @@ */ package jdk.jpackage.test; +import java.nio.file.Path; +import java.util.Objects; +import java.util.function.Supplier; + @FunctionalInterface public interface CannedArgument { + public String getValue(); + + public static CannedArgument create(Supplier supplier, String label) { + Objects.requireNonNull(supplier); + Objects.requireNonNull(label); + return new CannedArgument() { + + @Override + public String getValue() { + return supplier.get().toString(); + } + + @Override + public String toString( ) { + return label; + } + }; + } + + public static Object cannedAbsolutePath(Path v) { + return create(v::toAbsolutePath, String.format("AbsolutePath(%s)", v)); + } + + public static Object cannedAbsolutePath(String v) { + return cannedAbsolutePath(Path.of(v)); + } } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedFormattedString.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedFormattedString.java index 09f867da786..d5248ee2b17 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedFormattedString.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedFormattedString.java @@ -22,55 +22,29 @@ */ package jdk.jpackage.test; -import java.nio.file.Path; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.function.BiFunction; -import java.util.function.Supplier; import java.util.function.UnaryOperator; import java.util.stream.Stream; -public record CannedFormattedString(BiFunction formatter, String key, Object[] args) implements CannedArgument { - - public static CannedArgument cannedArgument(Supplier supplier, String label) { - Objects.requireNonNull(supplier); - Objects.requireNonNull(label); - return new CannedArgument() { - - @Override - public String getValue() { - return supplier.get().toString(); - } - - @Override - public String toString( ) { - return label; - } - }; - } - - public static Object cannedAbsolutePath(Path v) { - return cannedArgument(() -> v.toAbsolutePath(), String.format("AbsolutePath(%s)", v)); - } - - public static Object cannedAbsolutePath(String v) { - return cannedAbsolutePath(Path.of(v)); - } +public record CannedFormattedString(BiFunction formatter, String key, List args) implements CannedArgument { public CannedFormattedString mapArgs(UnaryOperator mapper) { - return new CannedFormattedString(formatter, key, Stream.of(args).map(mapper).toArray()); + return new CannedFormattedString(formatter, key, args.stream().map(mapper).toList()); } public CannedFormattedString { Objects.requireNonNull(formatter); Objects.requireNonNull(key); Objects.requireNonNull(args); - List.of(args).forEach(Objects::requireNonNull); + args.forEach(Objects::requireNonNull); } + @Override public String getValue() { - return formatter.apply(key, Stream.of(args).map(arg -> { + return formatter.apply(key, args.stream().map(arg -> { if (arg instanceof CannedArgument cannedArg) { return cannedArg.getValue(); } else { @@ -80,19 +54,29 @@ public record CannedFormattedString(BiFunction formatt } public CannedFormattedString addPrefix(String prefixKey) { - Objects.requireNonNull(prefixKey); - return new CannedFormattedString((theKey, theArgs) -> { - var str = formatter.apply((String)theArgs[0], Arrays.copyOfRange(theArgs, 1, theArgs.length)); - return formatter.apply(theKey, new Object[] {str}); - }, prefixKey, Stream.concat(Stream.of(key), Stream.of(args)).toArray()); + return new CannedFormattedString( + new AddPrefixFormatter(formatter), prefixKey, Stream.concat(Stream.of(key), args.stream()).toList()); } @Override public String toString() { - if (args.length == 0) { + if (args.isEmpty()) { return String.format("%s", key); } else { - return String.format("%s+%s", key, List.of(args)); + return String.format("%s+%s", key, args); + } + } + + private record AddPrefixFormatter(BiFunction formatter) implements BiFunction { + + AddPrefixFormatter { + Objects.requireNonNull(formatter); + } + + @Override + public String apply(String format, Object[] formatArgs) { + var str = formatter.apply((String)formatArgs[0], Arrays.copyOfRange(formatArgs, 1, formatArgs.length)); + return formatter.apply(format, new Object[] {str}); } } } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java index 2e58ea7a0ab..4fad120d0f6 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -43,6 +43,7 @@ import java.security.SecureRandom; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; @@ -60,6 +61,10 @@ import java.util.spi.ToolProvider; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; +import jdk.jpackage.internal.model.DottedVersion; +import jdk.jpackage.internal.util.MacBundle; +import jdk.jpackage.internal.util.Result; +import jdk.jpackage.internal.util.RuntimeReleaseFile; import jdk.jpackage.internal.util.function.ExceptionBox; import jdk.jpackage.internal.util.function.ThrowingConsumer; import jdk.jpackage.internal.util.function.ThrowingFunction; @@ -101,6 +106,7 @@ public class JPackageCommand extends CommandArguments { executeInDirectory = cmd.executeInDirectory; winMsiLogFile = cmd.winMsiLogFile; unpackedPackageDirectory = cmd.unpackedPackageDirectory; + explicitVersion = cmd.explicitVersion; } JPackageCommand createImmutableCopy() { @@ -245,11 +251,100 @@ public class JPackageCommand extends CommandArguments { public String version() { return PropertyFinder.findAppProperty(this, - PropertyFinder.cmdlineOptionWithValue("--app-version"), + PropertyFinder.of(Optional.ofNullable(explicitVersion)) + .or(PropertyFinder.cmdlineOptionWithValue("--app-version")) + .or(JPackageCommand::derivedVersion), PropertyFinder.appImageFile(appImageFile -> { return appImageFile.version(); }) - ).orElse("1.0"); + ).orElse(DEFAULT_VERSION); + } + + private Optional derivedVersion() { + if (isRuntime()) { + var predefinedRuntimePath = Path.of(getArgumentValue("--runtime-image")); + if (TKit.isOSX()) { + // This is a macOS runtime bundle. + return MacBundle.fromPath(predefinedRuntimePath).map(predefinedRuntimeBundle -> { + return Result.>of(() -> { + // This is a macOS runtime bundle created from the predefined runtime bundle (not a predefined runtime directory). + // The version of this bundle should be copied from the Info.plist file of the predefined runtime bundle. + return MacHelper.readPList(predefinedRuntimeBundle.infoPlistFile()).findValue("CFBundleVersion"); + }).value().flatMap(x -> x).or(() -> { + // Failed to read version from the Info.plist file of the predefined runtime bundle. + // Try to read it from the "release" file of the predefined runtime directory. + return normalizedVersionFromRuntimeReleaseFile(predefinedRuntimeBundle.homeDir()); + }); + }).orElseGet(() -> { + return normalizedVersionFromRuntimeReleaseFile(predefinedRuntimePath); + }); + } else { + return normalizedVersionFromRuntimeReleaseFile(predefinedRuntimePath); + } + } else { + return Optional.empty(); + } + } + + private Optional normalizedVersionFromRuntimeReleaseFile(Path runtimeDir) { + return Result.of(() -> { + return RuntimeReleaseFile.loadFromRuntime(runtimeDir).getJavaVersion().toString(); + }, Exception.class).value().map(JPackageCommand::normalizeDerivedVersion).map(map -> { + return Objects.requireNonNull(map.get(packageType())); + }); + } + + public static Map normalizeDerivedVersion(String version) { + var dotted = DottedVersion.lazy(version); + + var map = new HashMap(); + + // Linux + map.put(PackageType.LINUX_IMAGE, version); + map.put(PackageType.LINUX_DEB, version); + if (dotted.getUnprocessedSuffix().contains("-")) { + map.put(PackageType.LINUX_RPM, dotted.toComponentsString()); + } else { + map.put(PackageType.LINUX_RPM, version); + } + + // macOS + PackageType.ALL_MAC.forEach(type -> { + map.put(type, dotted.trim(3).pad(1).toComponentsString()); + }); + + // Windows + PackageType.ALL_WINDOWS.forEach(type -> { + DottedVersion ver; + if (dotted.getComponentsCount() < 2) { + ver = dotted.pad(2); + } else { + ver = dotted.trim(4); + } + map.put(type, ver.toComponentsString()); + }); + + map.put(PackageType.IMAGE, Objects.requireNonNull(map.get(PackageType.appImageForOS(PackageType.IMAGE.os())))); + + return map; + } + + /** + * Sets application version. + *

+ * Use this method to explicitly set the application version. Normally, the + * application version can be derived from the command line, but sometimes, when + * jpackage derives it from other sources, the {@code JPackageCommand} class + * can't get it correctly. Use this method in these uncommon cases. + * + * @param v the application version or {@code null} to reset previously set + * value + * @return this + */ + public JPackageCommand version(String v) { + verifyMutable(); + explicitVersion = v; + return this; } public String name() { @@ -297,7 +392,6 @@ public class JPackageCommand extends CommandArguments { } public JPackageCommand setFakeRuntime() { - verifyMutable(); addPrerequisiteAction(cmd -> { cmd.setArgumentValue("--runtime-image", createInputRuntimeImage(RuntimeImageType.RUNTIME_TYPE_FAKE)); }); @@ -305,12 +399,22 @@ public class JPackageCommand extends CommandArguments { return this; } + public JPackageCommand usePredefinedAppImage(JPackageCommand appImageCmd) { + appImageCmd.verifyIsOfType(PackageType.IMAGE); + verifyIsOfType(PackageType.IMAGE); + appImageCmd.getVerifyActionsWithRole(ActionRole.LAUNCHER_VERIFIER).forEach(verifier -> { + addVerifyAction(verifier, ActionRole.LAUNCHER_VERIFIER); + }); + return usePredefinedAppImage(appImageCmd.outputBundle()); + } + public JPackageCommand usePredefinedAppImage(Path predefinedAppImagePath) { return setArgumentValue("--app-image", Objects.requireNonNull(predefinedAppImagePath)) .removeArgumentWithValue("--input"); } JPackageCommand addPrerequisiteAction(ThrowingConsumer action) { + verifyMutable(); prerequisiteActions.add(action); return this; } @@ -326,6 +430,7 @@ public class JPackageCommand extends CommandArguments { } JPackageCommand addVerifyAction(ThrowingConsumer action, ActionRole actionRole) { + verifyMutable(); verifyActions.add(action, actionRole); return this; } @@ -954,13 +1059,13 @@ public class JPackageCommand extends CommandArguments { } public String getValue(CannedFormattedString str) { - return new CannedFormattedString(str.formatter(), str.key(), Stream.of(str.args()).map(arg -> { + return new CannedFormattedString(str.formatter(), str.key(), str.args().stream().map(arg -> { if (arg instanceof CannedArgument cannedArg) { return cannedArg.value(this); } else { return arg; } - }).toArray()).getValue(); + }).toList()).getValue(); } public JPackageCommand validateOut(CannedFormattedString... strings) { @@ -1918,6 +2023,7 @@ public class JPackageCommand extends CommandArguments { private Path executeInDirectory; private Path winMsiLogFile; private Path unpackedPackageDirectory; + private String explicitVersion; private Set readOnlyPathAsserts = Set.of(ReadOnlyPathAssert.values()); private Set standardAsserts = Set.of(StandardAssert.values()); private List> validators = new ArrayList<>(); @@ -1926,7 +2032,9 @@ public class JPackageCommand extends CommandArguments { VALUE } - private static final Map PACKAGE_TYPES = Stream.of(PackageType.values()).collect(toMap(PackageType::getType, x -> x)); + private static final Map PACKAGE_TYPES = Stream.of(PackageType.values()).filter(type -> { + return type.isNative() || type == PackageType.IMAGE; + }).collect(toMap(PackageType::getType, x -> x)); // Set the property to the path of run-time image to speed up // building app images and platform bundles by avoiding running jlink. @@ -1935,6 +2043,8 @@ public class JPackageCommand extends CommandArguments { // `--runtime-image` parameter set. public static final Path DEFAULT_RUNTIME_IMAGE = Optional.ofNullable(TKit.getConfigProperty("runtime-image")).map(Path::of).orElse(null); + public static final String DEFAULT_VERSION = "1.0"; + // [HH:mm:ss.SSS] private static final Pattern TIMESTAMP_REGEXP = Pattern.compile( "^\\[\\d\\d:\\d\\d:\\d\\d.\\d\\d\\d\\] "); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageOutputValidator.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageOutputValidator.java index c5c9c018b6a..ed14f40f65c 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageOutputValidator.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageOutputValidator.java @@ -43,7 +43,7 @@ public final class JPackageOutputValidator { } public JPackageOutputValidator(JPackageOutputValidator other) { - stdout = other.stdout; + stream = other.stream; match = other.match; matchTimestamps = other.matchTimestamps; stripTimestamps = other.stripTimestamps; @@ -59,7 +59,7 @@ public final class JPackageOutputValidator { * @return this */ public JPackageOutputValidator stdout() { - stdout = true; + stream = StdStream.OUT; return this; } @@ -68,7 +68,16 @@ public final class JPackageOutputValidator { * @return this */ public JPackageOutputValidator stderr() { - stdout = false; + stream = StdStream.ERR; + return this; + } + + /** + * Configures this validator to validate both stdout and stderr. + * @return this + */ + public JPackageOutputValidator stdoutAndStderr() { + stream = StdStream.OUT_AND_ERR; return this; } @@ -171,7 +180,7 @@ public final class JPackageOutputValidator { } public JPackageOutputValidator compose(JPackageOutputValidator other) { - if (stdout != other.stdout) { + if (stream != other.stream) { throw new IllegalArgumentException(); } if (match != other.match) { @@ -188,7 +197,7 @@ public final class JPackageOutputValidator { private Optional> toResultConsumer(JPackageCommand cmd) { return toStringIteratorConsumer(cmd).map(validator -> { - return toResultConsumer(validator, stdout, match, label()); + return toResultConsumer(validator, stream, match, label()); }); } @@ -244,7 +253,11 @@ public final class JPackageOutputValidator { } private String label() { - return stdout ? "'stdout'" : "'stderr'"; + return switch (stream) { + case OUT -> "'stdout'"; + case ERR -> "'stderr'"; + case OUT_AND_ERR -> "'stdout+stderr'"; + }; } private Consumer> decorate(TKit.TextStreamVerifier validator) { @@ -256,17 +269,16 @@ public final class JPackageOutputValidator { } private static Consumer toResultConsumer( - Consumer> validator, boolean stdout, boolean match, String label) { + Consumer> validator, StdStream stream, boolean match, String label) { Objects.requireNonNull(validator); Objects.requireNonNull(label); return result -> { - List content; - if (stdout) { - content = result.stdout(); - } else { - content = result.stderr(); - } + List content = switch (stream) { + case OUT -> result.stdout(); + case ERR -> result.stderr(); + case OUT_AND_ERR -> result.getOutput(); + }; if (match) { TKit.trace(String.format("Checking %s for exact match against defined validators...", label)); @@ -371,7 +383,14 @@ public final class JPackageOutputValidator { } } - boolean stdout = true; + private enum StdStream { + OUT, + ERR, + OUT_AND_ERR, + ; + } + + StdStream stream = StdStream.OUT; boolean match; boolean matchTimestamps; boolean stripTimestamps; diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageStringBundle.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageStringBundle.java index cd8ffd853b7..688b16a8e09 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageStringBundle.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageStringBundle.java @@ -31,6 +31,7 @@ import java.text.MessageFormat; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.function.BiFunction; import java.util.function.Function; import java.util.regex.Pattern; @@ -48,6 +49,9 @@ public enum JPackageStringBundle { } catch (ClassNotFoundException|NoSuchMethodException ex) { throw toUnchecked(ex); } + formatter = (String key, Object[] args) -> { + return new FormattedMessage(key, args).value(); + }; } /** @@ -61,12 +65,8 @@ public enum JPackageStringBundle { } } - private String getFormattedString(String key, Object[] args) { - return new FormattedMessage(key, args).value(); - } - public CannedFormattedString cannedFormattedString(String key, Object ... args) { - return new CannedFormattedString(this::getFormattedString, key, args); + return new CannedFormattedString(formatter, key, List.of(args)); } public Pattern cannedFormattedStringAsPattern(String key, Function formatArgMapper, Object ... args) { @@ -78,6 +78,10 @@ public enum JPackageStringBundle { }); } + public Pattern cannedFormattedStringAsPattern(String key, Object ... args) { + return cannedFormattedStringAsPattern(key, MATCH_ANY, args); + } + static Pattern toPattern(MessageFormat mf, Function formatArgMapper, Object ... args) { Objects.requireNonNull(mf); Objects.requireNonNull(formatArgMapper); @@ -153,4 +157,15 @@ public enum JPackageStringBundle { private final Class i18nClass; private final Method i18nClass_getString; + private final BiFunction formatter; + + private static final Function MATCH_ANY = new Function<>() { + + @Override + public Pattern apply(Object v) { + return PATTERN; + } + + private static final Pattern PATTERN = Pattern.compile(".*"); + }; } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java index f9fcfb905af..7657517bad5 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java @@ -372,14 +372,21 @@ public final class LauncherVerifier { TKit.assertTrue(entitlements.isPresent(), String.format("Check [%s] launcher is signed with entitlements", name)); + String expectedEntitlementsOrigin; + var customFile = Optional.ofNullable(cmd.getArgumentValue("--mac-entitlements")).map(Path::of); - if (customFile.isEmpty()) { + if (customFile.isPresent()) { + expectedEntitlementsOrigin = String.format("custom entitlements from [%s] file", customFile.get()); + } else { // Try from the resource dir. var resourceDirFile = Optional.ofNullable(cmd.getArgumentValue("--resource-dir")).map(Path::of).map(resourceDir -> { return resourceDir.resolve(cmd.name() + ".entitlements"); }).filter(Files::exists); if (resourceDirFile.isPresent()) { customFile = resourceDirFile; + expectedEntitlementsOrigin = "custom entitlements from the resource directory"; + } else { + expectedEntitlementsOrigin = null; } } @@ -388,11 +395,14 @@ public final class LauncherVerifier { expected = new PListReader(Files.readAllBytes(customFile.orElseThrow())).toMap(true); } else if (cmd.hasArgument("--mac-app-store")) { expected = DefaultEntitlements.APP_STORE; + expectedEntitlementsOrigin = "App Store entitlements"; } else { + expectedEntitlementsOrigin = "default entitlements"; expected = DefaultEntitlements.STANDARD; } - TKit.assertEquals(expected, entitlements.orElseThrow().toMap(true), String.format("Check [%s] launcher is signed with expected entitlements", name)); + TKit.assertEquals(expected, entitlements.orElseThrow().toMap(true), + String.format("Check [%s] launcher is signed with %s", name, expectedEntitlementsOrigin)); } private void executeLauncher(JPackageCommand cmd) throws IOException { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MsiDatabase.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MsiDatabase.java index 6b3fae07d41..6c92e820f2e 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MsiDatabase.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MsiDatabase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -30,7 +30,9 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -42,9 +44,10 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.IntStream; +import java.util.stream.Stream; -final class MsiDatabase { +public final class MsiDatabase { static MsiDatabase load(Path msiFile, Path idtFileOutputDir, Set tableNames) { try { @@ -61,7 +64,7 @@ final class MsiDatabase { .execute(0); var tables = orderedTableNames.stream().map(tableName -> { - return Map.entry(tableName, idtFileOutputDir.resolve(tableName + ".idt")); + return Map.entry(tableName, idtFileOutputDir.resolve(tableName.tableName() + ".idt")); }).filter(e -> { return Files.exists(e.getValue()); }).collect(Collectors.toMap(Map.Entry::getKey, e -> { @@ -81,6 +84,8 @@ final class MsiDatabase { FILE("File"), PROPERTY("Property"), SHORTCUT("Shortcut"), + CONTROL_EVENT("ControlEvent"), + INSTALL_UI_SEQUENCE("InstallUISequence"), ; Table(String name) { @@ -95,6 +100,7 @@ final class MsiDatabase { static final Set
FIND_PROPERTY_REQUIRED_TABLES = Set.of(PROPERTY); static final Set
LIST_SHORTCUTS_REQUIRED_TABLES = Set.of(COMPONENT, DIRECTORY, FILE, SHORTCUT); + static final Set
UI_ALTERATIONS_REQUIRED_TABLES = Set.of(CONTROL_EVENT, INSTALL_UI_SEQUENCE); } @@ -120,12 +126,7 @@ final class MsiDatabase { } Collection listShortcuts() { - var shortcuts = tables.get(Table.SHORTCUT); - if (shortcuts == null) { - return List.of(); - } - return IntStream.range(0, shortcuts.rowCount()).mapToObj(i -> { - var row = shortcuts.row(i); + return rows(Table.SHORTCUT).map(row -> { var shortcutPath = directoryPath(row.apply("Directory_")).resolve(fileNameFromFieldValue(row.apply("Name"))); var workDir = directoryPath(row.apply("WkDir")); var shortcutTarget = Path.of(expandFormattedString(row.apply("Target"))); @@ -133,6 +134,53 @@ final class MsiDatabase { }).toList(); } + UIAlterations uiAlterations() { + + var includeActions = Set.of("WelcomeEulaDlg", "WelcomeDlg"); + var actions = actionSequence(Table.INSTALL_UI_SEQUENCE).filter(action -> { + return includeActions.contains(action.action()); + }).sorted(Comparator.comparing(Action::sequence)).toList(); + + // Custom jpackage dialogs. + var jpackageDialogs = Set.of("InstallDirNotEmptyDlg", "ShortcutPromptDlg"); + + var includeControls = Set.of("Next", "Back"); + var controlEvents = rows(Table.CONTROL_EVENT).map(row -> { + return new ControlEvent( + row.apply("Dialog_"), + row.apply("Control_"), + row.apply("Event"), + row.apply("Argument"), + row.apply("Condition"), + Integer.parseInt(row.apply("Ordering"))); + }).filter(controlEvent -> { + if (jpackageDialogs.contains(controlEvent.dialog())) { + // Include controls of all custom jpackage dialogs. + return true; + } + + if (controlEvent.ordering() >= 6) { + // jpackage assumes the standard WiX UI doesn't define control events + // for dialog sequences it alters with the order higher than 6. + // Include all such items. + + if (includeControls.contains(controlEvent.control())) { + // Include only specific controls that jpackage alters. + return true; + } + } + + return false; + }).sorted(Comparator.comparing(ControlEvent::dialog) + .thenComparing(ControlEvent::control) + .thenComparing(ControlEvent::event) + .thenComparing(ControlEvent::argument) + .thenComparing(ControlEvent::condition) + .thenComparing(Comparator.comparingInt(ControlEvent::ordering))).toList(); + + return new UIAlterations(actions, controlEvents); + } + record Shortcut(Path path, Path target, Path workDir) { Shortcut { @@ -148,6 +196,49 @@ final class MsiDatabase { } } + public record Action(String action, String condition, int sequence) { + public Action { + Objects.requireNonNull(action); + Objects.requireNonNull(condition); + } + } + + public record ControlEvent( + String dialog, + String control, + String event, + String argument, + String condition, + int ordering) { + + public ControlEvent { + Objects.requireNonNull(dialog); + Objects.requireNonNull(control); + Objects.requireNonNull(event); + Objects.requireNonNull(argument); + Objects.requireNonNull(condition); + } + } + + public record UIAlterations(Collection installUISequence, Collection controlEvents) { + + public UIAlterations { + Objects.requireNonNull(installUISequence); + } + } + + private Stream actionSequence(Table tableName) { + return rows(tableName).map(row -> { + return new Action(row.apply("Action"), row.apply("Condition"), Integer.parseInt(row.apply("Sequence"))); + }); + } + + private Stream> rows(Table tableName) { + return Optional.ofNullable(tables.get(tableName)).stream().flatMap(table -> { + return IntStream.range(0, table.rowCount()).mapToObj(table::row); + }); + } + private Path directoryPath(String directoryId) { var table = tables.get(Table.DIRECTORY); Path result = null; @@ -339,20 +430,28 @@ final class MsiDatabase { var columns = headerLines.get(0).split("\t"); - var header = headerLines.get(2).split("\t", 4); - if (header.length == 3) { - if (Pattern.matches("^[1-9]\\d+$", header[0])) { - charset = Charset.forName(header[0]); - } else { - throw new IllegalArgumentException(String.format( - "Unexpected charset name [%s] in [%s] file", header[0], idtFile)); - } - } else if (header.length != 2) { + int tableNameIdx; + + var header = headerLines.get(2).split("\t"); + if (Pattern.matches("^[1-9]\\d+$", header[0])) { + charset = Charset.forName(header[0]); + tableNameIdx = 1; + } else { + tableNameIdx = 0; + } + + if (header.length < (tableNameIdx + 2)) { throw new IllegalArgumentException(String.format( "Unexpected number of fields (%d) in the 3rd line of [%s] file", header.length, idtFile)); } + var tableName = header[tableNameIdx]; + List primaryKeys = Arrays.asList(header).subList(tableNameIdx + 1, header.length); + + TKit.trace(String.format("Table in [%s] file: charset=%s; name=%s; primary keys=%s", + idtFile, charset, tableName, primaryKeys)); + return new IdtFileHeader(charset, List.of(columns)); } catch (IOException ex) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageType.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageType.java index 085e782ea40..df020d39fc8 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageType.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageType.java @@ -48,16 +48,22 @@ public enum PackageType { LINUX_RPM(".rpm", OperatingSystem.LINUX), MAC_DMG(".dmg", OperatingSystem.MACOS), MAC_PKG(".pkg", OperatingSystem.MACOS), - IMAGE; + IMAGE(OperatingSystem.current()), + WIN_IMAGE(OperatingSystem.WINDOWS), + LINUX_IMAGE(OperatingSystem.LINUX), + MAC_IMAGE(OperatingSystem.MACOS), + ; - PackageType() { + PackageType(OperatingSystem os) { + this.os = Objects.requireNonNull(os); type = "app-image"; suffix = null; - supported = true; - enabled = true; + supported = (os == OperatingSystem.current()); + enabled = supported; } PackageType(String packageName, String bundleSuffix, OperatingSystem os) { + this.os = Objects.requireNonNull(os); type = Objects.requireNonNull(packageName); suffix = Objects.requireNonNull(bundleSuffix); supported = isSupported(new BundlingOperationDescriptor(os, type)); @@ -88,10 +94,29 @@ public enum PackageType { return enabled; } + public boolean isAppImage() { + return type.equals(IMAGE.type); + } + + public boolean isNative() { + return !isAppImage(); + } + public String getType() { return type; } + public OperatingSystem os() { + return os; + } + + public static PackageType appImageForOS(OperatingSystem os) { + Objects.requireNonNull(os); + return Stream.of(LINUX_IMAGE, MAC_IMAGE, WIN_IMAGE).filter(type -> { + return type.os() == os; + }).findFirst().orElseThrow(); + } + public static RuntimeException throwSkippedExceptionIfNativePackagingUnavailable() { if (NATIVE.stream().noneMatch(PackageType::isSupported)) { TKit.throwSkippedException("None of the native packagers supported in this environment"); @@ -115,6 +140,7 @@ public enum PackageType { return new LinkedHashSet<>(List.of(types)); } + private final OperatingSystem os; private final String type; private final String suffix; private final boolean enabled; @@ -126,6 +152,10 @@ public enum PackageType { public static final Set NATIVE = Stream.of(LINUX, WINDOWS, MAC) .flatMap(Collection::stream).collect(Collectors.toUnmodifiableSet()); + public static final Set ALL_LINUX = orderedSet(LINUX_IMAGE, LINUX_DEB, LINUX_RPM); + public static final Set ALL_WINDOWS = orderedSet(WIN_IMAGE, WIN_MSI, WIN_EXE); + public static final Set ALL_MAC = orderedSet(MAC_IMAGE, MAC_DMG, MAC_PKG); + private static final class Inner { private static final Set DISABLED_PACKAGERS = Optional.ofNullable( TKit.tokenizeConfigProperty("disabledPackagers")).orElse( diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java index c7b55ed1de7..98e9c4bfe61 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java @@ -276,6 +276,11 @@ public class WindowsHelper { return MsiDatabaseCache.INSTANCE.findProperty(cmd.outputBundle(), propertyName).orElseThrow(); } + public static MsiDatabase.UIAlterations getUIAlterations(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.WIN_MSI); + return MsiDatabaseCache.INSTANCE.uiAlterations(cmd.outputBundle()); + } + static Collection getMsiShortcuts(JPackageCommand cmd) { cmd.verifyIsOfType(PackageType.WIN_MSI); return MsiDatabaseCache.INSTANCE.listShortcuts(cmd.outputBundle()); @@ -572,6 +577,10 @@ public class WindowsHelper { return ensureTables(msiPath, MsiDatabase.Table.LIST_SHORTCUTS_REQUIRED_TABLES).listShortcuts(); } + MsiDatabase.UIAlterations uiAlterations(Path msiPath) { + return ensureTables(msiPath, MsiDatabase.Table.UI_ALTERATIONS_REQUIRED_TABLES).uiAlterations(); + } + MsiDatabase ensureTables(Path msiPath, Set tableNames) { Objects.requireNonNull(msiPath); try { diff --git a/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/ActiveKeychainListTest.java b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/ActiveKeychainListTest.java new file mode 100644 index 00000000000..2ff5317e60d --- /dev/null +++ b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/ActiveKeychainListTest.java @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2026, 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 jdk.jpackage.internal; + +import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import jdk.jpackage.test.mock.CommandAction; +import jdk.jpackage.test.mock.CommandActionSpec; +import jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.CommandMockSpec; +import jdk.jpackage.test.mock.MockIllegalStateException; +import jdk.jpackage.test.mock.Script; +import jdk.jpackage.test.stdmock.JPackageMockUtils; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class ActiveKeychainListTest { + + @ParameterizedTest + @CsvSource(value = { + "'','',''", + "a,'',a", + "'',a,a", + "a,a,a", + "abc,b,abc", + "abc,ba,abc", + "abc,bad,abcd", + "ac,b,acb" + }) + void test_ctor_and_createForPlatform(String initial, String requested, String current) throws IOException { + + var initialKeychains = parseKeychainList(initial); + var requestedKeychains = parseKeychainList(requested); + + var securityMock = new SecurityKeychainListMock(true); + initialKeychains.stream().map(Keychain::name).forEach(securityMock.keychainNames()::add); + + Globals.main(toSupplier(() -> { + securityMock.applyToGlobals(); + Globals.instance().setProperty(ActiveKeychainList.class, false); + + assertTrue(ActiveKeychainList.createForPlatform(requestedKeychains.toArray(Keychain[]::new)).isEmpty()); + + var akl = new ActiveKeychainList(List.copyOf(requestedKeychains)); + try (akl) { + assertEquals(initialKeychains, akl.restoreKeychains()); + assertEquals(requestedKeychains, akl.requestedKeychains()); + assertEquals(parseKeychainList(current), akl.currentKeychains()); + assertEquals(akl.currentKeychains(), securityMock.keychains()); + } + + assertEquals(initialKeychains, akl.restoreKeychains()); + assertEquals(requestedKeychains, akl.requestedKeychains()); + assertEquals(parseKeychainList(current), akl.currentKeychains()); + assertEquals(initialKeychains, securityMock.keychains()); + + return 0; + })); + } + + @ParameterizedTest + @CsvSource(value = { + "'','','',true", + "'','','',false", + + "a,'','',true", + "a,'',a,false", + + "'',a,a,true", + "'',a,a,false", + + "a,a,a,true", + "a,a,a,false", + + "abc,b,b,true", + "abc,b,abc,false", + + "abc,ba,ba,true", + "abc,ba,abc,false", + + "abc,bad,bad,true", + "abc,bad,abcd,false", + + "ac,b,b,true", + "ac,b,acb,false" + }) + void testCtorWithForced(String initial, String requested, String current, boolean forced) throws IOException { + + var initialKeychains = parseKeychainList(initial); + var requestedKeychains = parseKeychainList(requested); + + var securityMock = new SecurityKeychainListMock(forced); + securityMock.keychainNames().addAll(List.of("foo", "bar")); + + Globals.main(toSupplier(() -> { + securityMock.applyToGlobals(); + + var akl = new ActiveKeychainList(List.copyOf(requestedKeychains), List.copyOf(initialKeychains), forced); + try (akl) { + assertEquals(initialKeychains, akl.restoreKeychains()); + assertEquals(requestedKeychains, akl.requestedKeychains()); + assertEquals(parseKeychainList(current), akl.currentKeychains()); + assertEquals(akl.currentKeychains(), securityMock.keychains()); + } + + assertEquals(initialKeychains, akl.restoreKeychains()); + assertEquals(requestedKeychains, akl.requestedKeychains()); + assertEquals(parseKeychainList(current), akl.currentKeychains()); + assertEquals(initialKeychains, securityMock.keychains()); + + return 0; + })); + } + + @ParameterizedTest + @CsvSource(value = { + "'','',", + "a,'',", + "'',a,a", + "a,a,a", + "abc,b,abc", + "abc,ba,abc", + "abc,bad,abcd", + "ac,b,acb" + }) + void test_withKeychain(String initial, String requested, String current) throws IOException { + + var initialKeychains = parseKeychainList(initial); + var requestedKeychains = parseKeychainList(requested); + + for (boolean isRequired : List.of(true, false)) { + var securityMock = new SecurityKeychainListMock(true); + initialKeychains.stream().map(Keychain::name).forEach(securityMock.keychainNames()::add); + + Consumer> workload = keychains -> { + assertEquals(requestedKeychains, keychains); + if (isRequired && current != null) { + assertEquals(parseKeychainList(current), securityMock.keychains()); + } else { + assertEquals(initialKeychains, securityMock.keychains()); + } + }; + + Globals.main(toSupplier(() -> { + Globals.instance().setProperty(ActiveKeychainList.class, isRequired); + + securityMock.applyToGlobals(); + ActiveKeychainList.withKeychains(workload, requestedKeychains); + + assertEquals(initialKeychains, securityMock.keychains()); + + if (requestedKeychains.size() == 1) { + securityMock.applyToGlobals(); + ActiveKeychainList.withKeychain(keychain -> { + workload.accept(List.of(keychain)); + }, requestedKeychains.getFirst()); + + assertEquals(initialKeychains, securityMock.keychains()); + } + + return 0; + })); + } + } + + /** + * Mocks "/usr/bin/security list-keychain" command. + */ + record SecurityKeychainListMock(List keychainNames, boolean isReadAllowed) implements CommandAction { + + SecurityKeychainListMock { + Objects.requireNonNull(keychainNames); + } + + SecurityKeychainListMock(boolean isReadAllowed) { + this(new ArrayList<>(), isReadAllowed); + } + + List keychains() { + return keychainNames.stream().map(Keychain::new).toList(); + } + + void applyToGlobals() { + CommandActionSpec actionSpec = CommandActionSpec.create("/usr/bin/security", this); + + var script = Script.build() + .commandMockBuilderMutator(mockBuilder -> { + // Limit the number of times the mock can be executed. + // It should be one or twice. + // Once, when ActiveKeychainList is constructed such that it doesn't read + // the current active keychain list from the "/usr/bin/security" command, but takes it from the parameter. + // Twice, when ActiveKeychainList is constructed such that it read + // the current active keychain list from the "/usr/bin/security" command. + mockBuilder.repeat(isReadAllowed ? 2 : 1); + }) + // Replace "/usr/bin/security" with the mock bound to the keychain mock. + .map(new CommandMockSpec(actionSpec.description(), "security-list-keychain", CommandActionSpecs.build().action(actionSpec).create())) + .createLoop(); + + JPackageMockUtils.buildJPackage() + .script(script) + .listener(System.out::println) + .applyToGlobals(); + } + + @Override + public Optional run(Context context) throws Exception, MockIllegalStateException { + final var origContext = context; + + if (!context.args().getFirst().equals("list-keychains")) { + throw origContext.unexpectedArguments(); + } + + context = context.shift(); + + if (context.args().isEmpty()) { + if (isReadAllowed) { + keychainNames.stream().map(k -> { + return new StringBuilder().append('"').append(k).append('"').toString(); + }).forEach(context::printlnOut); + } else { + throw origContext.unexpectedArguments(); + } + } else if (context.args().getFirst().equals("-s")) { + keychainNames.clear(); + keychainNames.addAll(context.shift().args()); + } else { + throw origContext.unexpectedArguments(); + } + + return Optional.of(0); + } + } + + private static List parseKeychainList(String str) { + return str.chars().mapToObj(chr -> { + return new StringBuilder().append((char)chr).toString(); + }).map(Keychain::new).toList(); + } +} diff --git a/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgPackagerTest.java b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgPackagerTest.java index 0e4893c8a06..3b71d53e2db 100644 --- a/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgPackagerTest.java +++ b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgPackagerTest.java @@ -175,13 +175,12 @@ public class MacDmgPackagerTest { private static void runPackagingMock(Path workDir, MacDmgSystemEnvironment sysEnv) { - var app = new ApplicationBuilder() + var appBuilder = new ApplicationBuilder() .appImageLayout(MacPackagingPipeline.APPLICATION_LAYOUT) .runtimeBuilder(createRuntimeBuilder()) - .name("foo") - .create(); + .name("foo"); - var macApp = new MacApplicationBuilder(app).create(); + var macApp = new MacApplicationBuilder(appBuilder).create(); var macDmgPkg = new MacDmgPackageBuilder(new MacPackageBuilder(new PackageBuilder(macApp, MAC_DMG))).create(); diff --git a/test/jdk/tools/jpackage/junit/macosx/junit.java b/test/jdk/tools/jpackage/junit/macosx/junit.java index 2253211add0..4ab05daf1ae 100644 --- a/test/jdk/tools/jpackage/junit/macosx/junit.java +++ b/test/jdk/tools/jpackage/junit/macosx/junit.java @@ -52,3 +52,14 @@ * jdk/jpackage/internal/MacDmgPackagerTest.java * @run junit jdk.jpackage/jdk.jpackage.internal.MacDmgPackagerTest */ + +/* @test + * @summary Test ActiveKeychainListTest + * @requires (os.family == "mac") + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.mock.* + * @build jdk.jpackage.test.stdmock.* + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/ActiveKeychainListTest.java + * @run junit jdk.jpackage/jdk.jpackage.internal.ActiveKeychainListTest + */ diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes index 32968db0606..19478aaa4a7 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes @@ -40,6 +40,7 @@ ErrorTest.test(WIN_MSI; app-desc=Hello; args-add=[--app-version, 1234]; errors=[ ErrorTest.test(WIN_MSI; app-desc=Hello; args-add=[--app-version, 256.1]; errors=[message.error-header+[error.msi-product-version-major-out-of-range], message.advice-header+[error.version-string-wrong-format.advice]]) ErrorTest.test(WIN_MSI; app-desc=Hello; args-add=[--launcher-as-service]; errors=[message.error-header+[error.missing-service-installer], message.advice-header+[error.missing-service-installer.advice]]) ErrorTest.test(args-add=[@foo]; errors=[message.error-header+[ERR_CannotParseOptions, foo]]) +ErrorTest.testMacSignAppStoreInvalidRuntime ErrorTest.testMacSignWithoutIdentity(IMAGE; app-desc=Hello; args-add=[--mac-sign, --mac-signing-keychain, @@EMPTY_KEYCHAIN@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, EMPTY_KEYCHAIN]]) ErrorTest.testMacSignWithoutIdentity(IMAGE; args-add=[--app-image, @@APP_IMAGE_WITH_SHORT_NAME@@, --mac-sign, --mac-signing-keychain, @@EMPTY_KEYCHAIN@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, EMPTY_KEYCHAIN]]) ErrorTest.testMacSignWithoutIdentity(MAC_DMG; app-desc=Hello; args-add=[--mac-sign, --mac-signing-keychain, @@EMPTY_KEYCHAIN@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, EMPTY_KEYCHAIN]]) diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/help-windows.txt b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/help-windows.txt index b72ba310f3f..7a098b847ad 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/help-windows.txt +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/help-windows.txt @@ -203,3 +203,5 @@ Platform dependent options for creating the application package: URL of available application update information --win-upgrade-uuid UUID associated with upgrades for this package + --win-with-ui + Enforces the installer to have UI diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/jpackage-options.md b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/jpackage-options.md index 60b79451547..59ae0d176c1 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/jpackage-options.md +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/jpackage-options.md @@ -29,7 +29,7 @@ | --linux-shortcut | linux-deb, linux-rpm | x | x | x | USE_LAST | | --mac-app-category | mac-bundle | x | x | | USE_LAST | | --mac-app-image-sign-identity | mac | x | x | | USE_LAST | -| --mac-app-store | mac-bundle | x | x | | USE_LAST | +| --mac-app-store | mac | x | x | | USE_LAST | | --mac-dmg-content | mac-dmg | x | x | | CONCATENATE | | --mac-entitlements | mac | x | x | | USE_LAST | | --mac-installer-sign-identity | mac-pkg | x | x | | USE_LAST | @@ -61,3 +61,4 @@ | --win-shortcut-prompt | win-exe, win-msi | x | x | | USE_LAST | | --win-update-url | win-exe, win-msi | x | x | | USE_LAST | | --win-upgrade-uuid | win-exe, win-msi | x | x | | USE_LAST | +| --win-with-ui | win-exe, win-msi | x | x | | USE_LAST | diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/DottedVersionTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/DottedVersionTest.java index df020f1a34c..65f9c71a910 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/DottedVersionTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/DottedVersionTest.java @@ -22,18 +22,19 @@ */ package jdk.jpackage.internal.model; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.junit.jupiter.api.Assertions.assertTrue; + import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.function.Function; import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrowsExactly; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertNotSame; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -54,14 +55,6 @@ public class DottedVersionTest { this(input, type.createVersion, "", expectedComponentCount, input); } - static TestConfig greedy(String input, int expectedComponentCount, String expectedToComponent) { - return new TestConfig(input, Type.GREEDY.createVersion, "", expectedComponentCount, expectedToComponent); - } - - static TestConfig greedy(String input, int expectedComponentCount) { - return new TestConfig(input, Type.GREEDY.createVersion, "", expectedComponentCount, input); - } - static TestConfig lazy(String input, String expectedSuffix, int expectedComponentCount, String expectedToComponent) { return new TestConfig(input, Type.LAZY.createVersion, expectedSuffix, expectedComponentCount, expectedToComponent); } @@ -74,6 +67,7 @@ public class DottedVersionTest { assertEquals(cfg.expectedSuffix(), dv.getUnprocessedSuffix()); assertEquals(cfg.expectedComponentCount(), dv.getComponents().length); assertEquals(cfg.expectedToComponent(), dv.toComponentsString()); + assertEquals(dv.toString(), cfg.input()); } private static List testValid() { @@ -123,7 +117,7 @@ public class DottedVersionTest { @ParameterizedTest @MethodSource - public void testTrim(DottedVersion ver, String expectedStr, int limit) { + public void test_trim(DottedVersion ver, String expectedStr, int limit) { var expected = DottedVersion.lazy(expectedStr); var actual = ver.trim(limit); assertEquals(expected, actual); @@ -136,14 +130,14 @@ public class DottedVersionTest { } @ParameterizedTest - @MethodSource - public void testTrimNegative(DottedVersion ver, int limit) { + @MethodSource("test_trim_pad_negative") + public void test_trim_negative(DottedVersion ver, int limit) { assertThrowsExactly(IllegalArgumentException.class, () -> { ver.trim(limit); }); } - private static Stream testTrim() { + private static Stream test_trim() { var testCases = new ArrayList(); @@ -160,15 +154,9 @@ public class DottedVersionTest { return testCases.stream().map(DottedVersionTest::mapFirstStringToDottedVersion); } - private static Stream testTrimNegative() { - return Stream.of( - Arguments.of("10.5.foo", -1) - ).map(DottedVersionTest::mapFirstStringToDottedVersion); - } - @ParameterizedTest @MethodSource - public void testPad(DottedVersion ver, String expectedStr, int limit) { + public void test_pad(DottedVersion ver, String expectedStr, int limit) { var expected = DottedVersion.lazy(expectedStr); var actual = ver.pad(limit); assertEquals(expected, actual); @@ -181,14 +169,14 @@ public class DottedVersionTest { } @ParameterizedTest - @MethodSource - public void testPadNegative(DottedVersion ver, int limit) { + @MethodSource("test_trim_pad_negative") + public void test_pad_negative(DottedVersion ver, int limit) { assertThrowsExactly(IllegalArgumentException.class, () -> { ver.pad(limit); }); } - private static Stream testPad() { + private static Stream test_pad() { var testCases = new ArrayList(); @@ -206,7 +194,7 @@ public class DottedVersionTest { return testCases.stream().map(DottedVersionTest::mapFirstStringToDottedVersion); } - private static Stream testPadNegative() { + private static Stream test_trim_pad_negative() { return Stream.of( Arguments.of("10.5.foo", -1) ).map(DottedVersionTest::mapFirstStringToDottedVersion); diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/RuntimeReleaseFileTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/RuntimeReleaseFileTest.java new file mode 100644 index 00000000000..097f63d51ab --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/RuntimeReleaseFileTest.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2026, 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 jdk.jpackage.internal.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.io.Writer; +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReference; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.Properties; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class RuntimeReleaseFileTest { + + @Test + void test_invalid_input(@TempDir Path workdir) { + assertThrows(IOException.class, () -> { + new RuntimeReleaseFile(workdir); + }); + } + + @Test + void test_findRawProperty(@TempDir Path workdir) throws IOException { + var releaseFile = new RuntimeReleaseFile(createPropFile(workdir, Map.of("Name", "John", "Company", "\"Acme LTD\""))); + + assertEquals(Optional.empty(), releaseFile.findRawProperty("foo")); + assertEquals(Optional.of("John"), releaseFile.findRawProperty("Name")); + assertEquals(Optional.of("\"Acme LTD\""), releaseFile.findRawProperty("Company")); + } + + @Test + void test_findProperty(@TempDir Path workdir) throws IOException { + var releaseFile = new RuntimeReleaseFile(createPropFile(workdir, Map.of("Name", "John", "Company", "\"Acme LTD\""))); + + assertEquals(Optional.empty(), releaseFile.findProperty("foo")); + assertEquals(Optional.of("John"), releaseFile.findProperty("Name")); + assertEquals(Optional.of("Acme LTD"), releaseFile.findProperty("Company")); + } + + @ParameterizedTest + @CsvSource({ + "foo, foo", + "\"foo\", foo", + "'foo', 'foo'", + "\"f\"o\"o\", f\"o\"o", + "\"foo, \"foo", + "foo\", foo\"", + }) + void test_findProperty(String rawValue, String expectedValue, @TempDir Path workdir) throws IOException { + var releaseFile = new RuntimeReleaseFile(createPropFile(workdir, Map.of("FOO", rawValue))); + + assertEquals(expectedValue, releaseFile.findProperty("FOO").orElseThrow()); + } + + @ParameterizedTest + @CsvSource({ + "\"27.1.2\", 27.1.2", + "27.1.2, 27.1.2", + "\"27.1.2-ea\", 27.1.2-ea", + "27.1.2-ea, 27.1.2-ea", + "\"27.1.2+15\", 27.1.2+15", + "27.1.2+15, 27.1.2+15", + }) + void test_getJavaVersion(String version, String expectedVersion, @TempDir Path workdir) throws IOException { + var releaseFile = new RuntimeReleaseFile(createPropFileWithValue(workdir, "JAVA_VERSION", version)); + + final var value = releaseFile.getJavaVersion(); + + assertEquals(expectedVersion, value.toString()); + } + + @ParameterizedTest + @CsvSource({ + "\"7.1.2+foo\"", + "\"foo\"", + "\"\"", + "7.1.2+foo", + "foo", + "''" + }) + void test_getJavaVersion_invalid(String version, @TempDir Path workdir) throws IOException { + var releaseFile = new RuntimeReleaseFile(createPropFileWithValue(workdir, "JAVA_VERSION", version)); + + var ex = assertThrows(RuntimeException.class, releaseFile::getJavaVersion); + + assertFalse(NoSuchElementException.class.isInstance(ex)); + } + + @Test + void test_without_version(@TempDir Path workdir) throws IOException { + var releaseFile = new RuntimeReleaseFile(createPropFileWithValue(workdir, "JDK_VERSION", "\"27.1.2\"")); + + assertThrowsExactly(NoSuchElementException.class, releaseFile::getJavaVersion); + } + + @Test + void test_getModules(@TempDir Path workdir) throws IOException { + var releaseFile = new RuntimeReleaseFile(createPropFileWithValue(workdir, "MODULES", "foo bar\t buz ")); + + assertEquals(List.of("foo", "bar", "buz"), releaseFile.getModules()); + } + + @Test + void test_current() throws IOException { + var releaseFile = new RuntimeReleaseFile(Path.of(System.getProperty("java.home")).resolve("release")); + + final var expectedVersion = Runtime.version(); + final var actualVersion = releaseFile.getJavaVersion(); + + assertEquals(expectedVersion.version(), actualVersion.version()); + + final var expectedModules = ModuleFinder.ofSystem().findAll().stream() + .map(ModuleReference::descriptor).map(ModuleDescriptor::name).sorted().toList(); + final var actualModules = releaseFile.getModules().stream().sorted().toList(); + + assertEquals(expectedModules, actualModules); + } + + private Path createPropFileWithValue(Path workdir, String name, String value) { + return createPropFile(workdir, Map.of(name, value)); + } + + private Path createPropFile(Path workdir, Map input) { + Path releaseFile = workdir.resolve("foo"); + Properties props = new Properties(); + props.putAll(input); + + try (Writer writer = Files.newBufferedWriter(releaseFile)) { + props.store(writer, null); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + + return releaseFile; + } +} diff --git a/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/WixVariablesTest.java b/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/WixVariablesTest.java new file mode 100644 index 00000000000..a9d83c8d544 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/WixVariablesTest.java @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2026, 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 jdk.jpackage.internal; + +import static jdk.jpackage.internal.WixToolset.WixToolsetType.Wix4; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import jdk.jpackage.internal.WixToolset.WixToolsetType; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; + +class WixVariablesTest { + + @Test + void test_define() { + assertEquals(List.of("-d", "foo=yes"), new WixVariables().define("foo").toWixCommandLine(Wix4)); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void test_define_null(boolean immutable) { + assertThrows(NullPointerException.class, vars -> { + vars.define(null); + }, create(immutable)); + } + + @Test + void test_put() { + assertEquals(List.of("-d", "foo=bar"), new WixVariables().put("foo", "bar").toWixCommandLine(Wix4)); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void test_put_null(boolean immutable) { + assertThrows(NullPointerException.class, vars -> { + vars.put("foo", null); + }, create(immutable)); + + assertThrows(NullPointerException.class, vars -> { + vars.put(null, "foo"); + }, create(immutable)); + } + + @Test + void test_putAll() { + assertEquals(List.of("-d", "foo=bar"), new WixVariables().putAll(Map.of("foo", "bar")).toWixCommandLine(Wix4)); + assertEquals(List.of("-d", "foo=yes"), new WixVariables().putAll(new WixVariables().define("foo")).toWixCommandLine(Wix4)); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void test_putAll_null(boolean immutable) { + + assertThrows(NullPointerException.class, vars -> { + vars.putAll((Map)null); + }, create(immutable)); + + assertThrows(NullPointerException.class, vars -> { + vars.putAll((WixVariables)null); + }, create(immutable)); + + final var expectedExceptionType = immutable ? IllegalStateException.class : NullPointerException.class; + + var other = new HashMap(); + + other.clear(); + other.put("foo", null); + assertThrows(expectedExceptionType, vars -> { + vars.putAll(other); + }, create(immutable)); + + other.clear(); + other.put(null, "foo"); + assertThrows(expectedExceptionType, vars -> { + vars.putAll(other); + }, create(immutable)); + } + + @Test + void testImmutable() { + var vars = new WixVariables().define("foo").createdImmutableCopy(); + + assertThrows(IllegalStateException.class, _ -> { + vars.putAll(Map.of()); + }, vars); + + assertThrows(IllegalStateException.class, _ -> { + vars.putAll(new WixVariables()); + }, vars); + + assertThrows(IllegalStateException.class, _ -> { + vars.define("foo"); + }, vars); + + assertThrows(IllegalStateException.class, _ -> { + vars.put("foo", "bar"); + }, vars); + + for (var allowOverrides : List.of(true, false)) { + assertThrows(IllegalStateException.class, _ -> { + vars.allowOverrides(allowOverrides); + }, vars); + } + } + + @Test + void testDefaultOverridable() { + var vars = new WixVariables().define("foo"); + + assertThrows(IllegalStateException.class, _ -> { + vars.define("foo"); + }, vars); + + assertThrows(IllegalStateException.class, _ -> { + vars.put("foo", "no"); + }, vars); + + assertThrows(IllegalStateException.class, _ -> { + vars.put("foo", "yes"); + }, vars); + + assertThrows(IllegalStateException.class, _ -> { + vars.putAll(Map.of("foo", "A", "bar", "B")); + }, vars); + + assertThrows(IllegalStateException.class, _ -> { + vars.putAll(new WixVariables().putAll(Map.of("foo", "A", "bar", "B"))); + }, vars); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testOverridable_define(boolean overridable) { + var vars = new WixVariables().allowOverrides(overridable).define("foo"); + + if (overridable) { + vars.define("foo"); + } else { + assertThrows(IllegalStateException.class, _ -> { + vars.define("foo"); + }, vars); + vars.allowOverrides(true); + vars.define("foo"); + } + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testOverridable_put(boolean overridable) { + var vars = new WixVariables().allowOverrides(overridable).define("foo"); + + if (overridable) { + vars.put("foo", "bar"); + assertEquals(List.of("-d", "foo=bar"), vars.toWixCommandLine(Wix4)); + } else { + assertThrows(IllegalStateException.class, _ -> { + vars.put("foo", "bar"); + }, vars); + vars.allowOverrides(true); + vars.put("foo", "bar"); + assertEquals(List.of("-d", "foo=bar"), vars.toWixCommandLine(Wix4)); + } + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testOverridable_putAll(boolean overridable) { + var vars = new WixVariables().allowOverrides(overridable).define("foo"); + + var other = Map.of("foo", "A", "bar", "B"); + + if (overridable) { + vars.putAll(other); + assertEquals(List.of("-d", "bar=B", "-d", "foo=A"), vars.toWixCommandLine(Wix4)); + } else { + assertThrows(IllegalStateException.class, _ -> { + vars.putAll(other); + }, vars); + vars.allowOverrides(true); + vars.putAll(other); + assertEquals(List.of("-d", "bar=B", "-d", "foo=A"), vars.toWixCommandLine(Wix4)); + } + } + + @Test + void test_createdImmutableCopy() { + var vars = new WixVariables().define("foo"); + + var copy = vars.createdImmutableCopy(); + + assertNotSame(vars, copy); + + assertSame(copy, copy.createdImmutableCopy()); + + assertEquals(List.of("-d", "foo=yes"), copy.toWixCommandLine(Wix4)); + + vars.allowOverrides(true).put("foo", "bar"); + assertEquals(List.of("-d", "foo=bar"), vars.toWixCommandLine(Wix4)); + assertEquals(List.of("-d", "foo=yes"), copy.toWixCommandLine(Wix4)); + } + + @ParameterizedTest + @EnumSource(WixToolsetType.class) + void test_toWixCommandLine(WixToolsetType wixType) { + var args = new WixVariables().define("foo").put("bar", "a").toWixCommandLine(wixType); + + var expectedArgs = switch (wixType) { + case Wix3 -> { + yield List.of("-dbar=a", "-dfoo=yes"); + } + case Wix4 -> { + yield List.of("-d", "bar=a", "-d", "foo=yes"); + } + }; + + assertEquals(expectedArgs, args); + } + + private static WixVariables create(boolean immutable) { + var vars = new WixVariables(); + if (immutable) { + return vars.createdImmutableCopy(); + } else { + return vars; + } + } + + private static void assertThrows( + Class expectedExceptionType, Consumer mutator, WixVariables vars) { + + var content = vars.toWixCommandLine(Wix4); + + assertThrowsExactly(expectedExceptionType, () -> { + mutator.accept(vars); + }); + + assertEquals(content, vars.toWixCommandLine(Wix4)); + } +} diff --git a/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/wixui/UISpecTest.java b/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/wixui/UISpecTest.java new file mode 100644 index 00000000000..08e7796ca50 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/wixui/UISpecTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2026, 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 jdk.jpackage.internal.wixui; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +class UISpecTest { + + @ParameterizedTest + @MethodSource + void test(UIConfig cfg) { + var uiSpec = UISpec.create(cfg); + + validateCustomDialogSequence(uiSpec.customDialogSequence()); + } + + private static Collection test() { + + var testCases = new ArrayList(); + + for (boolean withInstallDirChooserDlg : List.of(true, false)) { + for (boolean withShortcutPromptDlg : List.of(true, false)) { + for (boolean withLicenseDlg : List.of(true, false)) { + testCases.add(UIConfig.build() + .withInstallDirChooserDlg(withInstallDirChooserDlg) + .withShortcutPromptDlg(withShortcutPromptDlg) + .withLicenseDlg(withLicenseDlg) + .create()); + } + } + } + + return testCases; + } + + static void validateCustomDialogSequence(Map seq) { + seq.entrySet().stream().map(DialogControl::new).collect(Collectors.toMap(x -> x, x -> x, (a, b) -> { + throw new AssertionError(String.format( + "Dialog [%s] has multiple Publish elements associated with [%s] control", a.host(), a.hostedControl())); + })); + } + + record DialogControl(Dialog host, Control hostedControl) { + DialogControl { + Objects.requireNonNull(host); + Objects.requireNonNull(hostedControl); + } + + DialogControl(DialogPair pair, Publish publish) { + this(pair.first(), publish.control()); + } + + DialogControl(Map.Entry e) { + this(e.getKey(), e.getValue()); + } + } +} diff --git a/test/jdk/tools/jpackage/junit/windows/junit.java b/test/jdk/tools/jpackage/junit/windows/junit.java index d27a282f0dc..1a1d0d58f7e 100644 --- a/test/jdk/tools/jpackage/junit/windows/junit.java +++ b/test/jdk/tools/jpackage/junit/windows/junit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -28,3 +28,20 @@ * jdk/jpackage/internal/ExecutableOSVersionTest.java * @run junit jdk.jpackage/jdk.jpackage.internal.ExecutableOSVersionTest */ + +/* @test + * @summary WixVariables unit tests + * @requires (os.family == "windows") + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/WixVariablesTest.java + * @run junit jdk.jpackage/jdk.jpackage.internal.WixVariablesTest + */ + +/* @test + * @summary UiSpec unit tests + * @requires (os.family == "windows") + * @modules jdk.jpackage/jdk.jpackage.internal.wixui:open + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/wixui/UISpecTest.java + * @run junit jdk.jpackage/jdk.jpackage.internal.wixui.UISpecTest + */ diff --git a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java index 3a257a425b6..fc146270ab8 100644 --- a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java @@ -64,7 +64,6 @@ public class SigningAppImageTest { var testAL = new AdditionalLauncher("testAL"); testAL.applyTo(cmd); - cmd.executeAndAssertHelloAppImageCreated(); MacSign.withKeychain(keychain -> { sign.addTo(cmd); diff --git a/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java b/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java index a6d94b59bd9..20af9572044 100644 --- a/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java @@ -26,6 +26,7 @@ import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.jpackage.test.AdditionalLauncher; @@ -68,6 +69,22 @@ public class SigningAppImageTwoStepsTest { spec.test(); } + @Test + public static void testAppStore() { + + var sign = new SignKeyOptionWithKeychain( + SignKeyOption.Type.SIGN_KEY_USER_SHORT_NAME, + SigningBase.StandardCertificateRequest.CODESIGN, + SigningBase.StandardKeychain.MAIN.keychain()); + + var spec = new TestSpec(Optional.empty(), sign); + + spec.signAppImage(spec.createAppImage(), Optional.of(cmd -> { + cmd.addArgument("--mac-app-store"); + })); + } + + public record TestSpec(Optional signAppImage, SignKeyOptionWithKeychain sign) { public TestSpec { @@ -133,7 +150,7 @@ public class SigningAppImageTwoStepsTest { private SignKeyOptionWithKeychain sign; } - void test() { + JPackageCommand createAppImage() { var appImageCmd = JPackageCommand.helloAppImage() .setFakeRuntime() .setArgumentValue("--dest", TKit.createTempDirectory("appimage")); @@ -150,16 +167,29 @@ public class SigningAppImageTwoStepsTest { }, signOption.keychain()); }, appImageCmd::execute); + return appImageCmd; + } + + void signAppImage(JPackageCommand appImageCmd, Optional> mutator) { + Objects.requireNonNull(appImageCmd); + Objects.requireNonNull(mutator); + MacSign.withKeychain(keychain -> { var cmd = new JPackageCommand() .setPackageType(PackageType.IMAGE) - .addArguments("--app-image", appImageCmd.outputBundle()) + .usePredefinedAppImage(appImageCmd) .mutate(sign::addTo); + mutator.ifPresent(cmd::mutate); + cmd.executeAndAssertHelloAppImageCreated(); MacSignVerify.verifyAppImageSigned(cmd, sign.certRequest()); }, sign.keychain()); } + + void test() { + signAppImage(createAppImage(), Optional.empty()); + } } public static Collection test() { diff --git a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java b/test/jdk/tools/jpackage/macosx/SigningPackageTest.java index d1c17fb61cd..cc2e7026d72 100644 --- a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningPackageTest.java @@ -65,7 +65,7 @@ import jdk.jpackage.test.PackageType; * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=SigningPackageTest.test * --jpt-space-subst=* - * --jpt-include=({--mac-signing-key-user-name:*CODESIGN},*{--mac-signing-key-user-name:*PKG},*MAC_DMG+MAC_PKG) + * --jpt-include=({KEY_USER_NAME:*CODESIGN},*{KEY_USER_NAME:*PKG},*MAC_DMG+MAC_PKG) * --jpt-before-run=SigningBase.verifySignTestEnvReady */ diff --git a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java index 11b466ed715..42a0e6fd664 100644 --- a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java @@ -37,6 +37,8 @@ import jdk.jpackage.internal.util.Slot; import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.JPackageOutputValidator; +import jdk.jpackage.test.JPackageStringBundle; import jdk.jpackage.test.MacHelper; import jdk.jpackage.test.MacHelper.ResolvableCertificateRequest; import jdk.jpackage.test.MacHelper.SignKeyOption; @@ -98,6 +100,13 @@ public class SigningRuntimeImagePackageTest { cmd.ignoreDefaultRuntime(true); cmd.removeArgumentWithValue("--input"); cmd.setArgumentValue("--runtime-image", predefinedRuntime.get()); + + // `warning.per.user.app.image.signed` warning doesn't apply to runtime bundling. + // Ensure the warning is not in the output. + new JPackageOutputValidator().add(TKit.assertTextStream( + JPackageStringBundle.MAIN.cannedFormattedStringAsPattern("warning.per.user.app.image.signed", "file") + ).negate()).stdoutAndStderr().applyTo(cmd); + }).addInstallVerifier(cmd -> { MacSignVerify.verifyAppImageSigned(cmd, signRuntime.certRequest()); }).run(); diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license+shortcut_prompt/ControlEvents.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license+shortcut_prompt/ControlEvents.md new file mode 100644 index 00000000000..39e6b535e69 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license+shortcut_prompt/ControlEvents.md @@ -0,0 +1,8 @@ +| Dialog | Control | Event | Argument | Condition | Ordering | +| --- | --- | --- | --- | --- | --- | +| InstallDirNotEmptyDlg | No | NewDialog | InstallDirDlg | 1 | 1 | +| InstallDirNotEmptyDlg | Yes | NewDialog | ShortcutPromptDlg | 1 | 1 | +| ShortcutPromptDlg | Back | NewDialog | InstallDirDlg | 1 | 1 | +| ShortcutPromptDlg | Cancel | SpawnDialog | CancelDlg | 1 | 1 | +| ShortcutPromptDlg | Next | NewDialog | VerifyReadyDlg | 1 | 1 | +| VerifyReadyDlg | Back | NewDialog | ShortcutPromptDlg | NOT Installed | 6 | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license+shortcut_prompt/InstallUISequence.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license+shortcut_prompt/InstallUISequence.md new file mode 100644 index 00000000000..e583dfa4982 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license+shortcut_prompt/InstallUISequence.md @@ -0,0 +1,3 @@ +| Action | Condition | +| --- | --- | +| WelcomeDlg | NOT Installed OR PATCH | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license/ControlEvents.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license/ControlEvents.md new file mode 100644 index 00000000000..1d908b0508a --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license/ControlEvents.md @@ -0,0 +1,4 @@ +| Dialog | Control | Event | Argument | Condition | Ordering | +| --- | --- | --- | --- | --- | --- | +| InstallDirNotEmptyDlg | No | NewDialog | InstallDirDlg | 1 | 1 | +| InstallDirNotEmptyDlg | Yes | NewDialog | VerifyReadyDlg | 1 | 1 | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license/InstallUISequence.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license/InstallUISequence.md new file mode 100644 index 00000000000..e583dfa4982 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license/InstallUISequence.md @@ -0,0 +1,3 @@ +| Action | Condition | +| --- | --- | +| WelcomeDlg | NOT Installed OR PATCH | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+shortcut_prompt/ControlEvents.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+shortcut_prompt/ControlEvents.md new file mode 100644 index 00000000000..93d11c944e0 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+shortcut_prompt/ControlEvents.md @@ -0,0 +1,10 @@ +| Dialog | Control | Event | Argument | Condition | Ordering | +| --- | --- | --- | --- | --- | --- | +| InstallDirDlg | Back | NewDialog | WelcomeDlg | NOT Installed | 6 | +| InstallDirNotEmptyDlg | No | NewDialog | InstallDirDlg | 1 | 1 | +| InstallDirNotEmptyDlg | Yes | NewDialog | ShortcutPromptDlg | 1 | 1 | +| ShortcutPromptDlg | Back | NewDialog | InstallDirDlg | 1 | 1 | +| ShortcutPromptDlg | Cancel | SpawnDialog | CancelDlg | 1 | 1 | +| ShortcutPromptDlg | Next | NewDialog | VerifyReadyDlg | 1 | 1 | +| VerifyReadyDlg | Back | NewDialog | ShortcutPromptDlg | NOT Installed | 6 | +| WelcomeDlg | Next | NewDialog | InstallDirDlg | NOT Installed | 6 | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+shortcut_prompt/InstallUISequence.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+shortcut_prompt/InstallUISequence.md new file mode 100644 index 00000000000..e583dfa4982 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+shortcut_prompt/InstallUISequence.md @@ -0,0 +1,3 @@ +| Action | Condition | +| --- | --- | +| WelcomeDlg | NOT Installed OR PATCH | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser/ControlEvents.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser/ControlEvents.md new file mode 100644 index 00000000000..2fcc387732a --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser/ControlEvents.md @@ -0,0 +1,6 @@ +| Dialog | Control | Event | Argument | Condition | Ordering | +| --- | --- | --- | --- | --- | --- | +| InstallDirDlg | Back | NewDialog | WelcomeDlg | NOT Installed | 6 | +| InstallDirNotEmptyDlg | No | NewDialog | InstallDirDlg | 1 | 1 | +| InstallDirNotEmptyDlg | Yes | NewDialog | VerifyReadyDlg | 1 | 1 | +| WelcomeDlg | Next | NewDialog | InstallDirDlg | NOT Installed | 6 | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser/InstallUISequence.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser/InstallUISequence.md new file mode 100644 index 00000000000..e583dfa4982 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser/InstallUISequence.md @@ -0,0 +1,3 @@ +| Action | Condition | +| --- | --- | +| WelcomeDlg | NOT Installed OR PATCH | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license+shortcut_prompt/ControlEvents.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license+shortcut_prompt/ControlEvents.md new file mode 100644 index 00000000000..72e8b80ca08 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license+shortcut_prompt/ControlEvents.md @@ -0,0 +1,7 @@ +| Dialog | Control | Event | Argument | Condition | Ordering | +| --- | --- | --- | --- | --- | --- | +| LicenseAgreementDlg | Next | NewDialog | ShortcutPromptDlg | LicenseAccepted = "1" | 6 | +| ShortcutPromptDlg | Back | NewDialog | LicenseAgreementDlg | 1 | 1 | +| ShortcutPromptDlg | Cancel | SpawnDialog | CancelDlg | 1 | 1 | +| ShortcutPromptDlg | Next | NewDialog | VerifyReadyDlg | 1 | 1 | +| VerifyReadyDlg | Back | NewDialog | ShortcutPromptDlg | NOT Installed | 6 | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license+shortcut_prompt/InstallUISequence.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license+shortcut_prompt/InstallUISequence.md new file mode 100644 index 00000000000..e583dfa4982 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license+shortcut_prompt/InstallUISequence.md @@ -0,0 +1,3 @@ +| Action | Condition | +| --- | --- | +| WelcomeDlg | NOT Installed OR PATCH | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license/ControlEvents.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license/ControlEvents.md new file mode 100644 index 00000000000..7a9bcca86f3 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license/ControlEvents.md @@ -0,0 +1,2 @@ +| Dialog | Control | Event | Argument | Condition | Ordering | +| --- | --- | --- | --- | --- | --- | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license/InstallUISequence.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license/InstallUISequence.md new file mode 100644 index 00000000000..3008e0db54b --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license/InstallUISequence.md @@ -0,0 +1,4 @@ +| Action | Condition | +| --- | --- | +| WelcomeDlg | Installed AND PATCH | +| WelcomeEulaDlg | NOT Installed | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/shortcut_prompt/ControlEvents.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/shortcut_prompt/ControlEvents.md new file mode 100644 index 00000000000..a93b0ecbff1 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/shortcut_prompt/ControlEvents.md @@ -0,0 +1,7 @@ +| Dialog | Control | Event | Argument | Condition | Ordering | +| --- | --- | --- | --- | --- | --- | +| ShortcutPromptDlg | Back | NewDialog | WelcomeDlg | NOT Installed | 6 | +| ShortcutPromptDlg | Cancel | SpawnDialog | CancelDlg | 1 | 1 | +| ShortcutPromptDlg | Next | NewDialog | VerifyReadyDlg | 1 | 1 | +| VerifyReadyDlg | Back | NewDialog | ShortcutPromptDlg | NOT Installed | 6 | +| WelcomeDlg | Next | NewDialog | ShortcutPromptDlg | NOT Installed | 6 | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/shortcut_prompt/InstallUISequence.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/shortcut_prompt/InstallUISequence.md new file mode 100644 index 00000000000..e583dfa4982 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/shortcut_prompt/InstallUISequence.md @@ -0,0 +1,3 @@ +| Action | Condition | +| --- | --- | +| WelcomeDlg | NOT Installed OR PATCH | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/ui/ControlEvents.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/ui/ControlEvents.md new file mode 100644 index 00000000000..7a9bcca86f3 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/ui/ControlEvents.md @@ -0,0 +1,2 @@ +| Dialog | Control | Event | Argument | Condition | Ordering | +| --- | --- | --- | --- | --- | --- | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/ui/InstallUISequence.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/ui/InstallUISequence.md new file mode 100644 index 00000000000..6e3eef39f43 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/ui/InstallUISequence.md @@ -0,0 +1,4 @@ +| Action | Condition | +| --- | --- | +| WelcomeDlg | Installed AND PATCH | +| WelcomeEulaDlg | 0 | diff --git a/test/jdk/tools/jpackage/resources/msi-export.js b/test/jdk/tools/jpackage/resources/msi-export.js index d639f19ca44..6cb5ab5a781 100644 --- a/test/jdk/tools/jpackage/resources/msi-export.js +++ b/test/jdk/tools/jpackage/resources/msi-export.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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,7 +71,7 @@ function exportTables(db, outputDir, requestedTableNames) { var msi = WScript.arguments(0) var outputDir = WScript.arguments(1) var tables = {} - for (var i = 0; i !== WScript.arguments.Count(); i++) { + for (var i = 2; i !== WScript.arguments.Count(); i++) { tables[WScript.arguments(i)] = true } diff --git a/test/jdk/tools/jpackage/share/AppVersionTest.java b/test/jdk/tools/jpackage/share/AppVersionTest.java index ad96a9b9e0c..7ba1870def1 100644 --- a/test/jdk/tools/jpackage/share/AppVersionTest.java +++ b/test/jdk/tools/jpackage/share/AppVersionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -22,15 +22,49 @@ */ +import static java.util.stream.Collectors.toUnmodifiableMap; +import static java.util.stream.Collectors.toUnmodifiableSet; +import static jdk.jpackage.internal.util.PListWriter.writeDict; +import static jdk.jpackage.internal.util.PListWriter.writePList; +import static jdk.jpackage.internal.util.XmlUtils.createXml; +import static jdk.jpackage.internal.util.XmlUtils.toXmlConsumer; +import static jdk.jpackage.test.JPackageCommand.DEFAULT_VERSION; +import static jdk.jpackage.test.JPackageCommand.normalizeDerivedVersion; +import static jdk.jpackage.test.JPackageCommand.RuntimeImageType.RUNTIME_TYPE_FAKE; + import java.io.IOException; -import java.util.Collection; +import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; -import javax.xml.xpath.XPathExpressionException; -import jdk.jpackage.test.AppImageFile; -import jdk.jpackage.test.Annotations.Parameters; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Stream; +import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.util.MacBundle; +import jdk.jpackage.internal.util.RuntimeReleaseFile; +import jdk.jpackage.internal.util.Slot; +import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.AppImageFile; +import jdk.jpackage.test.CannedFormattedString; +import jdk.jpackage.test.ConfigurationTarget; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.JPackageCommand.StandardAssert; +import jdk.jpackage.test.JPackageOutputValidator; +import jdk.jpackage.test.JPackageStringBundle; +import jdk.jpackage.test.JavaAppDesc; +import jdk.jpackage.test.MacHelper; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.RunnablePackageTest.Action; import jdk.jpackage.test.TKit; /* @@ -45,54 +79,952 @@ import jdk.jpackage.test.TKit; public final class AppVersionTest { - @Parameters - public static Collection input() { - List data = new ArrayList<>(); + @Test + @ParameterSupplier + public static void testApp(AppTestSpec testSpec) { - data.addAll(List.of(new Object[][]{ - // Default jpackage version - {"1.0", "Hello", null}, - {"1.0", "com.other/com.other.Hello", null}, - // Version should be picked from --app-version - {"3.1", "Hello", new String[]{"--app-version", "3.1"}}, - {"3.2", "com.other/com.other.Hello", new String[]{"--app-version", - "3.2"}}, - // Version should be picked from the last --app-version - {"3.3", "Hello", new String[]{"--app-version", "4", "--app-version", - "3.3"}}, - {"7.8", "com.other/com.other.Hello", new String[]{"--app-version", - "4", "--app-version", "7.8"}}, - // Pick version from jar - {"3.10.17", "com.other/com.other.Hello@3.10.17", null}, - // Ignore version in jar if --app-version given - {"7.5.81", "com.other/com.other.Hello@3.10.17", new String[]{ - "--app-version", "7.5.81"}} - })); + var moduleVersionSource = testSpec.findVersionSource(ModuleVersionSource.class); - return data; - } + ConfigurationTarget cfg; + if (testSpec.isImagePackageType()) { + var cmd = moduleVersionSource.map(ModuleVersionSource::appDesc) + .map(JPackageCommand::helloAppImage) + .orElseGet(JPackageCommand::helloAppImage); + cfg = new ConfigurationTarget(cmd); + } else { + var nativeTest = new PackageTest().forTypes(testSpec.spec().expected().keySet()); + moduleVersionSource + .map(ModuleVersionSource::appDesc) + .ifPresentOrElse(nativeTest::configureHelloApp, nativeTest::configureHelloApp); - public AppVersionTest(String expectedVersion, String javaAppDesc, - String[] jpackageArgs) { - this.expectedVersion = expectedVersion; - this.javaAppDesc = javaAppDesc; - this.jpackageArgs = jpackageArgs; + cfg = new ConfigurationTarget(nativeTest); + } + + cfg.addInitializer(JPackageCommand::setFakeRuntime); + + cfg.addInitializer(testSpec::applyTo); + + cfg.cmd().ifPresent(JPackageCommand::executeAndAssertHelloAppImageCreated); + + testSpec.validateVersion(cfg); + + cfg.test().ifPresent(pkg -> { + pkg.run(testSpec.spec().packageTestActions()); + }); } @Test - public void test() throws XPathExpressionException, IOException { - JPackageCommand cmd = JPackageCommand.helloAppImage(javaAppDesc); - if (jpackageArgs != null) { - cmd.addArguments(jpackageArgs); - } - cmd.executeAndAssertHelloAppImageCreated(); + @ParameterSupplier + @ParameterSupplier(value = "testMacPredefinedRuntimeBundle", ifOS = OperatingSystem.MACOS) + public static void testRuntime(RuntimeTestSpec testSpec) { - String actualVersion = AppImageFile.load(cmd.outputBundle()).version(); - TKit.assertEquals(expectedVersion, actualVersion, - "Check application version"); + var predefinedRuntimeDir = Slot.createEmpty(); + new PackageTest() + .forTypes(testSpec.spec().expected().keySet()) + .addRunOnceInitializer(() -> { + predefinedRuntimeDir.set(testSpec.createRuntime()); + }) + .addInitializer(cmd -> { + cmd.removeArgumentWithValue("--input").setArgumentValue("--runtime-image", predefinedRuntimeDir.get()); + }) + .addInitializer(testSpec::applyTo) + .mutate(test -> { + testSpec.validateVersion(new ConfigurationTarget(test)); + }) + .run(testSpec.spec().packageTestActions()); } - private final String expectedVersion; - private final String javaAppDesc; - private final String[] jpackageArgs; + public static Collection testApp() { + + List testCases = new ArrayList<>(); + + for (var modular : List.of(true, false)) { + String appDesc; + if (modular) { + appDesc = "com.other/com.other.Hello"; + } else { + appDesc = "Hello"; + } + + // Default version. + AppTestSpec.create(appDesc, TestSpec.build().expectDefaultVersion(), testCases::add); + + // Pick version from the command line. + AppTestSpec.create(appDesc, TestSpec.build().versionFromCmdline("3.1"), testCases::add); + } + + // Pick version from the modular jar. + AppTestSpec.create(TestSpec.build() + .versionFromAppModule("com.other/com.other.Hello@3.10.16"), testCases::add); + + // Pick version from the command line, ignore version of the modular jar. + AppTestSpec.create(TestSpec.build() + .versionFromAppModule("com.other/com.other.Hello@3.10.18") + .versionFromCmdline("7.5.81"), testCases::add); + + // Pick version from the modular jar. Apply package-specific normalization. + for (var ver : List.of( + "30.10.17.204.899-foo" + )) { + var versionSource = new ModuleVersionSource("com.other/com.other.Hello@" + ver); + + var builder = TestSpec.build().versionSource(versionSource); + for (var e : TestSpec.Builder.getExpectedVersions(versionSource).entrySet()) { + builder.withTypes(e.getKey()).expect(e.getValue()); + } + + AppTestSpec.create(builder, testCases::add); + } + + return testCases.stream().map(v -> { + return new Object[] {v}; + }).toList(); + } + + public static Collection testRuntime() { + + List testCases = new ArrayList<>(); + + // Default version. + RuntimeTestSpec.create(TestSpec.build().expectDefaultVersion(), testCases::add); + + // Pick version from the command line. + RuntimeTestSpec.create(TestSpec.build().versionFromCmdline("3.1"), testCases::add); + + // Invalid versions. + for (var ver : List.of("foo", "", "17.21.3+foo")) { + RuntimeTestSpec.create(TestSpec.build().versionFromReleaseFile(ver).expectDefaultVersion(), testCases::add); + } + + // Valid version values (see java.lang.Runtime.Version javadoc and https://openjdk.org/jeps/223) + for (var suffix : jep223VersionSuffixes()) { + for (var vnum : List.of("17", "17.1", "17.1.2", "17.1.2.3", "17.1.2.3.5")) { + var ver = new RuntimeReleaseFileVersionSource(vnum + suffix); + + var builder = TestSpec.build().versionSource(ver); + for (var e : TestSpec.Builder.getExpectedVersions(ver).entrySet()) { + builder.withTypes(e.getKey()).expect(e.getValue()); + } + + RuntimeTestSpec.create(builder, testCases::add); + } + } + + return testCases.stream().map(v -> { + return new Object[] {v}; + }).toList(); + } + + public static Collection testMacPredefinedRuntimeBundle() { + + List testCases = new ArrayList<>(); + + var appendTestCases = skipImagePackageType(testSpec -> { + for (var runtimeType : List.of( + RuntimeType.MAC_BUNDLE_PLIST_FILE_MALFORMED, + RuntimeType.MAC_BUNDLE_PLIST_WITHOUT_VERSION + )) { + testCases.add(new RuntimeTestSpec(runtimeType, testSpec)); + } + }); + + // Invalid version. + TestSpec.build().versionFromReleaseFile("foo").expectDefaultVersion().create(appendTestCases); + + // Valid versions. + for (var suffix : List.of("", "-foo")) { + for (var vnum : List.of("17", "17.1", "17.1.2", "17.1.2.3")) { + var ver = new RuntimeReleaseFileVersionSource(vnum + suffix); + + var builder = TestSpec.build().versionSource(ver); + var allBundleTypes = TestSpec.Builder.getExpectedVersions(ver); + for (var bundleType : PackageType.MAC) { + builder.withTypes(bundleType).expect(Objects.requireNonNull(allBundleTypes.get(bundleType))); + } + + builder.create(appendTestCases); + } + } + + return testCases.stream().map(v -> { + return new Object[] {v}; + }).toList(); + } + + private static List jep223VersionSuffixes() { + var suffixes = new HashSet(); + for (var pre : List.of("", "-ea")) { + for (var build : List.of("", "+5678")) { + for (var opt : List.of("", "-foo", "-12.UZ3")) { + if (pre.isEmpty() && build.isEmpty() && !opt.isEmpty()) { + suffixes.add("+" + opt); + } else { + suffixes.add(pre + build + opt); + } + } + } + } + return suffixes.stream().sorted().peek(suffix -> { + // Validate version suffixes. + Runtime.Version.parse("11" + suffix); + }).toList(); + } + + enum Message { + VERSION_FROM_MODULE("message.module-version", "version", "module"), + VERSION_FROM_RELEASE_FILE("message.release-version", "version"), + VERSION_NORMALIZED("message.version-normalized", "version", "version"), + ; + + Message(String key, Object ... args) { + this.key = Objects.requireNonNull(key); + this.args = args; + } + + CannedFormattedString cannedFormattedString(Object ... args) { + return JPackageStringBundle.MAIN.cannedFormattedString(key, args); + } + + TKit.TextStreamVerifier negateFind() { + var pattern = JPackageStringBundle.MAIN.cannedFormattedStringAsPattern(key, args); + return TKit.assertTextStream(pattern).negate(); + } + + String key() { + return key; + } + + private final String key; + private final Object[] args; + } + + sealed interface VersionSource { + String version(); + + VersionSource copyWithVersion(String v); + } + + enum DefaultVersionSource implements VersionSource { + INSTANCE; + + @Override + public String version() { + return DEFAULT_VERSION; + } + + @Override + public VersionSource copyWithVersion(String v) { + return this; + } + } + + record ModuleVersionSource(String appDesc) implements VersionSource { + + ModuleVersionSource { + Objects.requireNonNull(appDesc); + if (JavaAppDesc.parse(appDesc).moduleVersion() == null) { + throw new IllegalArgumentException(); + } + } + + @Override + public String version() { + return JavaAppDesc.parse(appDesc).moduleVersion(); + } + + @Override + public VersionSource copyWithVersion(String v) { + return new ModuleVersionSource(moduleName() + "@" + Objects.requireNonNull(v)); + } + + String moduleName() { + return JavaAppDesc.parse(appDesc).moduleName(); + } + + @Override + public String toString() { + return appDesc; + } + } + + record CmdlineVersionSource(String version) implements VersionSource { + + CmdlineVersionSource { + Objects.requireNonNull(version); + } + + @Override + public VersionSource copyWithVersion(String v) { + return new CmdlineVersionSource(v); + } + + @Override + public String toString() { + return String.format("--app-version=[%s]", version); + } + } + + record RuntimeReleaseFileVersionSource(String version) implements VersionSource { + + RuntimeReleaseFileVersionSource { + Objects.requireNonNull(version); + } + + @Override + public VersionSource copyWithVersion(String v) { + return new RuntimeReleaseFileVersionSource(v); + } + + @Override + public String toString() { + return String.format("JAVA_VERSION=[%s]", version); + } + } + + record Expected(String version, List messages) { + + Expected { + Objects.requireNonNull(version); + Objects.requireNonNull(messages); + } + + void applyTo(JPackageCommand cmd) { + Objects.requireNonNull(cmd); + new JPackageOutputValidator().expectMatchingStrings(messages).matchTimestamps().stripTimestamps().applyTo(cmd); + cmd.version(version); + + var expectMessageKeys = messages.stream().map(CannedFormattedString::key).toList(); + Stream.of(Message.values()).filter(message -> { + return !expectMessageKeys.contains(message.key()); + }).map(Message::negateFind).forEach(validator -> { + new JPackageOutputValidator().add(validator).stdoutAndStderr().applyTo(cmd); + }); + } + + @Override + public String toString() { + var sb = new StringBuilder(); + sb.append(version); + if (!messages.isEmpty()) { + sb.append("; ").append(messages); + } + return sb.toString(); + } + + static Builder build() { + return new Builder(); + } + + static final class Builder { + + Expected create() { + return new Expected(version, List.copyOf(messages)); + } + + Builder version(String v) { + version = v; + return this; + } + + Builder messages(List v) { + messages.addAll(v); + return this; + } + + Builder messages(CannedFormattedString... v) { + return messages(List.of(v)); + } + + Builder message(Message message, Object ... args) { + return messages(message.cannedFormattedString(args)); + } + + private String version; + private final List messages = new ArrayList<>(); + } + } + + record TestSpec(Collection versions, Map expected) { + + TestSpec { + Objects.requireNonNull(versions); + Objects.requireNonNull(expected); + + if (expected.isEmpty()) { + throw new IllegalArgumentException(); + } + + if (expected.keySet().contains(PackageType.IMAGE) && !Collections.disjoint(expected.keySet(), PackageType.NATIVE)) { + throw new IllegalArgumentException("Mixing of native and app image packaging"); + } + + if (expected.keySet().stream().map(PackageType::os).distinct().count() != 1) { + throw new IllegalArgumentException("All package types must be for the same OS"); + } + } + + Optional findVersionSource(Class versionSourceType) { + Objects.requireNonNull(versionSourceType); + return versions.stream().filter(versionSourceType::isInstance).map(versionSourceType::cast).findFirst(); + } + + void applyTo(JPackageCommand cmd) { + Objects.requireNonNull(cmd); + findVersionSource(CmdlineVersionSource.class).ifPresent(ver -> { + cmd.setArgumentValue("--app-version", ver.version()); + }); + expected.get(cmd.packageType()).applyTo(cmd); + } + + void validateVersion(ConfigurationTarget cfg) { + cfg.cmd().ifPresent(cmd -> { + var actualVersion = AppImageFile.load(cmd.outputBundle()).version(); + TKit.assertEquals(expected.get(cmd.packageType()).version(), actualVersion, "Check application version"); + }); + cfg.test().ifPresent(test -> { + expected.entrySet().forEach(e -> { + nativeBundleVersionPropertyName(e.getKey()).ifPresent(propertyName -> { + test.forTypes(e.getKey(), _ -> { + test.addBundlePropertyVerifier(propertyName, e.getValue().version()); + }); + }); + }); + }); + + if (os() == OperatingSystem.MACOS) { + cfg.addInstallVerifier(cmd -> { + final var bundleRoot = cmd.isImagePackageType() ? cmd.outputBundle() + : cmd.pathToUnpackedPackageFile(cmd.appInstallationDirectory()); + var plist = MacHelper.readPListFromAppImage(bundleRoot); + var expectedVersion = expected.get(cmd.packageType()).version(); + for (var prop : List.of("CFBundleVersion", "CFBundleShortVersionString")) { + TKit.assertEquals(expectedVersion, plist.queryValue(prop), + String.format("Check the value of '%s' property in [%s] bundle", prop, bundleRoot)); + } + }); + } + } + + boolean isImagePackageType() { + return expected.keySet().contains(PackageType.IMAGE); + } + + Action[] packageTestActions() { + if (os() == OperatingSystem.MACOS) { + return Action.CREATE_AND_UNPACK; + } else { + return new Action[] {Action.CREATE}; + } + } + + OperatingSystem os() { + return expected.keySet().iterator().next().os(); + } + + @Override + public String toString() { + var sb = new StringBuilder(); + + switch (versions.size()) { + case 0 -> { + } + case 1 -> { + sb.append(versions.iterator().next()).append("; "); + } + default -> { + sb.append("versions=").append(versions).append("; "); + } + } + + sb.append("expect="); + if (expected.values().stream().distinct().count() == 1) { + sb.append(expected.keySet().stream().sorted().toList()).append(":"); + sb.append(expected.values().iterator().next()); + } else { + sb.append('[').append(expected).append(']'); + } + + return sb.toString(); + } + + private static Optional nativeBundleVersionPropertyName(PackageType type) { + switch (type) { + case LINUX_DEB, LINUX_RPM -> { + return Optional.of("Version"); + } + case WIN_MSI -> { + return Optional.of("ProductVersion"); + } + default -> { + return Optional.empty(); + } + } + } + + static Builder build() { + return new Builder(); + } + + static final class Builder { + + Builder() { + } + + Builder(Builder other) { + currentTypes = other.currentTypes; + currentExpectedVersion = other.currentExpectedVersion; + pendingCommit = other.pendingCommit; + versions.addAll(other.versions); + expected.putAll(other.expected); + } + + Builder copy() { + return new Builder(this); + } + + void create(Consumer sink) { + Objects.requireNonNull(sink); + if (pendingCommit) { + copy().expect(expectedVersion()).commit().create(sink); + } else { + var types = expected.keySet(); + var copiedVersions = List.copyOf(versions); + if (types.contains(PackageType.IMAGE) && !Collections.disjoint(types, PackageType.NATIVE)) { + sink.accept(new TestSpec(copiedVersions, Map.of(PackageType.IMAGE, expected.get(PackageType.IMAGE)))); + var copy = new HashMap<>(expected); + copy.remove(PackageType.IMAGE); + sink.accept(new TestSpec(copiedVersions, copy)); + } else { + new TestSpec(copiedVersions, Map.copyOf(expected)); + } + } + } + + Builder versionSource(VersionSource v) { + if (Objects.requireNonNull(v) == DefaultVersionSource.INSTANCE) { + throw new IllegalArgumentException(); + } + versions.add(v); + pendingCommit = true; + return this; + } + + Builder versionFromCmdline(String v) { + return versionSource(new CmdlineVersionSource(v)); + } + + Builder versionFromAppModule(String v) { + return versionSource(new ModuleVersionSource(v)); + } + + Builder versionFromReleaseFile(String v) { + return versionSource(new RuntimeReleaseFileVersionSource(v)); + } + + Builder expect(VersionSource v) { + currentExpectedVersion = v; + pendingCommit = true; + return this; + } + + Builder expectDefaultVersion() { + return expect(DefaultVersionSource.INSTANCE); + } + + Builder expectVersionFromCmdline(String v) { + return expect(new CmdlineVersionSource(v)); + } + + Builder expectVersionFromAppModule(String appDesc) { + return expect(new ModuleVersionSource(appDesc)); + } + + Builder expectVersionFromReleaseFile(String v) { + return expect(new RuntimeReleaseFileVersionSource(v)); + } + + Builder withTypes(Set types) { + if (types.isEmpty()) { + types = ALL_TYPES; + } + + if (!currentTypes.equals(types)) { + commit(); + currentTypes = types; + } + + return this; + } + + Builder withTypes(PackageType ... types) { + return withTypes(Set.of(types)); + } + + static Map getExpectedVersions(VersionSource versionSource) { + var map = new HashMap(normalizeDerivedVersion(versionSource.version()).entrySet() + .stream() + .collect(toUnmodifiableMap(Map.Entry::getKey, e -> { + return versionSource.copyWithVersion(e.getValue()); + }) + )); + + ALL_TYPES.forEach(type -> { + map.putIfAbsent(type, DefaultVersionSource.INSTANCE); + }); + + return map; + } + + private VersionSource expectedVersion() { + return Optional.ofNullable(currentExpectedVersion).or(() -> { + return versions.stream().filter(CmdlineVersionSource.class::isInstance).findFirst(); + }).or(() -> { + if (versions.size() == 1) { + return Optional.of(versions.getFirst()); + } else { + return Optional.empty(); + } + }).orElseThrow(IllegalStateException::new); + } + + private Builder commit() { + pendingCommit = false; + + if (versions.isEmpty() && currentExpectedVersion == null) { + // Nothing to commit. + return this; + } + + var filteredTypes = normalize(currentTypes); + if (filteredTypes.isEmpty()) { + // Version configuration is not supported on the current OS. + return this; + } + + VersionSource expectedVersion = expectedVersion(); + + VersionSource versionSource; + if (expectedVersion == DefaultVersionSource.INSTANCE) { + versionSource = expectedVersion; + } else { + versionSource = versions.stream().filter(expectedVersion.getClass()::isInstance).findFirst().orElseThrow(); + } + + var expectedBuilder = Expected.build().version(expectedVersion.version()); + switch (versionSource) { + case ModuleVersionSource ver -> { + expectedBuilder.message(Message.VERSION_FROM_MODULE, ver.version(), ver.moduleName()); + } + case RuntimeReleaseFileVersionSource ver -> { + expectedBuilder.message(Message.VERSION_FROM_RELEASE_FILE, ver.version()); + } + default -> { + // NOP + } + } + + if (!versionSource.version().equals(expectedVersion.version())) { + expectedBuilder.message(Message.VERSION_NORMALIZED, expectedVersion.version(), versionSource.version()); + } + + var expectedValue = expectedBuilder.create(); + filteredTypes.forEach(type -> { + expected.put(type, expectedValue); + }); + + return this; + } + + private static Set normalize(Collection types) { + return types.stream().filter(type -> { + return type.os() == OperatingSystem.current(); + }) + // Filter out "exe" packaging as it is a duplicate of "msi" packaging and + // the testing lib can't validate properties of embedded msi file. + .filter(Predicate.isEqual(PackageType.WIN_EXE).negate()) + .map(type -> { + if (type.isAppImage()) { + return PackageType.IMAGE; + } else { + return type; + } + }).collect(toUnmodifiableSet()); + } + + private Set currentTypes = ALL_TYPES; + private VersionSource currentExpectedVersion; + private boolean pendingCommit; + private final List versions = new ArrayList<>(); + private final Map expected = new HashMap<>(); + + private static final Set ALL_TYPES = Set.of(PackageType.values()); + } + } + + record AppTestSpec(String appDesc, TestSpec spec) { + + AppTestSpec { + Objects.requireNonNull(appDesc); + Objects.requireNonNull(spec); + } + + AppTestSpec(TestSpec spec) { + this(spec.findVersionSource(ModuleVersionSource.class).orElseThrow().appDesc(), spec); + } + + static void create(String appDesc, TestSpec.Builder specBuilder, Consumer sink) { + Objects.requireNonNull(appDesc); + Objects.requireNonNull(sink); + specBuilder.create(spec -> { + sink.accept(new AppTestSpec(appDesc, spec)); + }); + } + + static void create(TestSpec.Builder specBuilder, Consumer sink) { + Objects.requireNonNull(sink); + specBuilder.create(spec -> { + sink.accept(new AppTestSpec(spec)); + }); + } + + Optional findVersionSource(Class versionSourceType) { + return spec.findVersionSource(versionSourceType); + } + + void applyTo(JPackageCommand cmd) { + spec.applyTo(cmd); + } + + void validateVersion(ConfigurationTarget cfg) { + spec.validateVersion(cfg); + } + + boolean isImagePackageType() { + return spec.isImagePackageType(); + } + + @Override + public String toString() { + var sb = new StringBuilder(); + if (spec.findVersionSource(ModuleVersionSource.class).map(ModuleVersionSource::appDesc).filter(appDesc::equals).isEmpty()) { + sb.append("app-desc=").append(appDesc()).append("; "); + } + sb.append(spec); + return sb.toString(); + } + } + + /** + * Type of the predefined runtime. + */ + enum RuntimeType { + /** + * A directory with the standard Java runtime structure. + */ + IMAGE, + /** + * macOS bundle with valid Info.plist file. + */ + MAC_BUNDLE, + /** + * macOS bundle with malformed Info.plist file. + */ + MAC_BUNDLE_PLIST_FILE_MALFORMED, + /** + * macOS bundle with Info.plist file without version. + */ + MAC_BUNDLE_PLIST_WITHOUT_VERSION, + ; + } + + record RuntimeTestSpec(RuntimeType type, TestSpec spec) { + + RuntimeTestSpec { + Objects.requireNonNull(type); + Objects.requireNonNull(spec); + if (spec.isImagePackageType()) { + throw new IllegalArgumentException(); + } + if (type == RuntimeType.MAC_BUNDLE && spec.os() != OperatingSystem.MACOS) { + throw new IllegalArgumentException("Bundle runtime is supported for macOS native packaging only"); + } + } + + static void create(TestSpec.Builder specBuilder, Consumer sink) { + Objects.requireNonNull(sink); + specBuilder.create(skipImagePackageType(spec -> { + if (spec.os() != OperatingSystem.MACOS) { + sink.accept(new RuntimeTestSpec(RuntimeType.IMAGE, spec)); + } else { + sink.accept(new RuntimeTestSpec(RuntimeType.IMAGE, spec)); + + if (spec.findVersionSource(CmdlineVersionSource.class).isPresent()) { + // Disable "AppVersionTest.testRuntime(mac_bundle; versions=[--app-version=[3.1], plist=[1.22.333]]; expect=[MAC_DMG, MAC_PKG]:3.1)" test for now + // as it will fail because jpackage halfway ignores the "--app-version" + // and any option in general that makes its way into the Info.plist file. + // When building a runtime from the predefined runtime bundle, + // jpackage copies the Info.plist file from the predefined runtime bundle into the output bundle verbatim. + return; + } + + var plistVersionSource = new InheritPListVersionSource(MAC_PREDEFINED_RUNTIME_BUNDLE_VERSION); + var specBuilderCopy = specBuilder.copy().versionSource(plistVersionSource).withTypes(PackageType.ALL_MAC); + if (spec.findVersionSource(CmdlineVersionSource.class).isEmpty()) { + specBuilderCopy.expect(plistVersionSource); + } + specBuilderCopy.create(skipImagePackageType(augmentedSpec -> { + sink.accept(new RuntimeTestSpec(RuntimeType.MAC_BUNDLE, augmentedSpec)); + })); + } + })); + } + + Optional findVersionSource(Class versionSourceType) { + return spec.findVersionSource(versionSourceType); + } + + void applyTo(JPackageCommand cmd) { + switch (type) { + case MAC_BUNDLE_PLIST_FILE_MALFORMED, MAC_BUNDLE_PLIST_WITHOUT_VERSION -> { + // Don't validate signature of the output bundle. If the Info.plist file is malformed, it will fail with the error: + // + // [22:59:25.374] TRACE: Command [/usr/bin/codesign --verify --deep --strict --verbose=2 RuntimeAppVersionTest.jdk](6) exited with exit code 3 and the following output: + // [22:59:25.375] TRACE: 2026-03-05 22:59:25.353 codesign[11351:61024] There was an error parsing the Info.plist for the bundle at URL <0x7f81e140ae70>: NSCocoaErrorDomain - 3840 + // [22:59:25.376] TRACE: 2026-03-05 22:59:25.370 codesign[11351:61024] There was an error parsing the Info.plist for the bundle at URL <0x7f81e1413b90>: NSCocoaErrorDomain - 3840 + // [22:59:25.376] TRACE: --prepared:RuntimeAppVersionTest.jdk/Contents/MacOS/libjli.dylib + // [22:59:25.377] TRACE: --validated:RuntimeAppVersionTest.jdk/Contents/MacOS/libjli.dylib + // + cmd.excludeStandardAsserts(StandardAssert.MAC_BUNDLE_UNSIGNED_SIGNATURE); + // This one will also fail. + cmd.excludeStandardAsserts(StandardAssert.MAC_RUNTIME_PLIST_JDK_KEY); + } + default -> { + // NOP + } + } + spec.applyTo(cmd); + } + + void validateVersion(ConfigurationTarget cfg) { + switch (type) { + case MAC_BUNDLE_PLIST_FILE_MALFORMED, MAC_BUNDLE_PLIST_WITHOUT_VERSION -> { + cfg.addInstallVerifier(cmd -> { + final var bundleRoot = cmd.isImagePackageType() ? cmd.outputBundle() + : cmd.pathToUnpackedPackageFile(cmd.appInstallationDirectory()); + final var infoPlist = new MacBundle(bundleRoot).infoPlistFile(); + TKit.assertFileExists(infoPlist); + TKit.trace(String.format("Bundle version property in [%s] file is unavailable. Skip validation", infoPlist)); + }); + } + default -> { + spec.validateVersion(cfg); + } + } + } + + Path createRuntime() throws IOException { + return createRuntime(type, findVersionSource(RuntimeReleaseFileVersionSource.class).map(VersionSource::version)); + } + + @Override + public String toString() { + return new StringBuilder().append(type.name().toLowerCase()).append("; ").append(spec).toString(); + } + + private record InheritPListVersionSource(String version) implements VersionSource { + + InheritPListVersionSource { + Objects.requireNonNull(version); + } + + @Override + public VersionSource copyWithVersion(String v) { + return new InheritPListVersionSource(v); + } + + @Override + public String toString() { + return String.format("plist=[%s]", version); + } + } + + private Path createRuntime(RuntimeType type, Optional releaseFileVersion) throws IOException { + Objects.requireNonNull(type); + Objects.requireNonNull(releaseFileVersion); + + Path predefinedRuntimeDir; + Path runtimeDir = switch (type) { + case IMAGE -> { + predefinedRuntimeDir = JPackageCommand.createInputRuntimeImage(RUNTIME_TYPE_FAKE); + yield predefinedRuntimeDir; + } + case MAC_BUNDLE -> { + releaseFileVersion.ifPresent(ver -> { + if (ver.equals(MAC_PREDEFINED_RUNTIME_BUNDLE_VERSION)) { + // The value of `JAVA_VERSION` property in the "release" file of the runtime + // and the value of the `CFBundleVersion` property in the plist file should be different + // to test corner cases of version picking logic. + throw new IllegalArgumentException(); + } + }); + + // Create macOS bundle with `MAC_PREDEFINED_RUNTIME_BUNDLE_VERSION` version. + // The idea is to have different values of `JAVA_VERSION` property in the "release" file + // of the runtime and the value of the `CFBundleVersion` property in the plist file. + predefinedRuntimeDir = MacHelper.buildRuntimeBundle().type(RUNTIME_TYPE_FAKE).mutator(cmd -> { + cmd.setArgumentValue("--app-version", MAC_PREDEFINED_RUNTIME_BUNDLE_VERSION); + }).create(); + yield MacBundle.fromPath(predefinedRuntimeDir).orElseThrow().homeDir(); + } + case MAC_BUNDLE_PLIST_FILE_MALFORMED, MAC_BUNDLE_PLIST_WITHOUT_VERSION -> { + predefinedRuntimeDir = MacHelper.buildRuntimeBundle().type(RUNTIME_TYPE_FAKE).create(); + var plistFile = new MacBundle(predefinedRuntimeDir).infoPlistFile(); + + switch (type) { + case MAC_BUNDLE_PLIST_FILE_MALFORMED -> { + TKit.trace(String.format("Create invalid plist file [%s]", plistFile)); + } + case MAC_BUNDLE_PLIST_WITHOUT_VERSION -> { + TKit.trace(String.format("Create empty plist file [%s]", plistFile)); + } + default -> { + throw new AssertionError(); + } + } + + createXml(plistFile, xml -> { + writePList(xml, toXmlConsumer(() -> { + if (type == RuntimeType.MAC_BUNDLE_PLIST_WITHOUT_VERSION) { + writeDict(xml, toXmlConsumer(() -> { + })); + } + })); + }); + yield MacBundle.fromPath(predefinedRuntimeDir).orElseThrow().homeDir(); + } + default -> { + throw new AssertionError(); + } + }; + + releaseFileVersion.ifPresent(ver -> { + TKit.createPropertiesFile( + RuntimeReleaseFile.releaseFilePathInRuntime(runtimeDir), + Map.of("JAVA_VERSION", "\"" + ver + "\"")); + }); + + return predefinedRuntimeDir; + } + + static final String MAC_PREDEFINED_RUNTIME_BUNDLE_VERSION = "1.22.333"; + } + + private static Consumer skipImagePackageType(Consumer consumer) { + Objects.requireNonNull(consumer); + return spec -> { + if (!spec.isImagePackageType()) { + consumer.accept(spec); + } + }; + } } diff --git a/test/jdk/tools/jpackage/share/ErrorTest.java b/test/jdk/tools/jpackage/share/ErrorTest.java index b325c02f039..fbaec8283e8 100644 --- a/test/jdk/tools/jpackage/share/ErrorTest.java +++ b/test/jdk/tools/jpackage/share/ErrorTest.java @@ -26,7 +26,6 @@ import static java.util.stream.Collectors.toMap; import static jdk.internal.util.OperatingSystem.LINUX; import static jdk.internal.util.OperatingSystem.MACOS; import static jdk.internal.util.OperatingSystem.WINDOWS; -import static jdk.jpackage.internal.util.PListWriter.writeDict; import static jdk.jpackage.internal.util.PListWriter.writePList; import static jdk.jpackage.internal.util.XmlUtils.createXml; import static jdk.jpackage.internal.util.XmlUtils.toXmlConsumer; @@ -35,6 +34,7 @@ import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; import static jdk.jpackage.test.JPackageCommand.makeAdvice; import static jdk.jpackage.test.JPackageCommand.makeError; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -56,6 +56,7 @@ import jdk.jpackage.internal.util.TokenReplace; import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.ApplicationLayout; import jdk.jpackage.test.CannedArgument; import jdk.jpackage.test.CannedFormattedString; import jdk.jpackage.test.JPackageCommand; @@ -140,11 +141,9 @@ public final class ErrorTest { var appImageDir = (Path)APP_IMAGE.expand(cmd).orElseThrow(); // Replace the default Info.plist file with an empty one. var plistFile = new MacBundle(appImageDir).infoPlistFile(); - TKit.trace(String.format("Create invalid plist file in [%s]", plistFile)); + TKit.trace(String.format("Create invalid plist file [%s]", plistFile)); createXml(plistFile, xml -> { writePList(xml, toXmlConsumer(() -> { - writeDict(xml, toXmlConsumer(() -> { - })); })); }); return appImageDir; @@ -701,11 +700,48 @@ public final class ErrorTest { )); } + @Test(ifOS = MACOS) + public static void testMacSignAppStoreInvalidRuntime() throws IOException { + + // Create app image with the runtime directory content that will fail the subsequent signing jpackage command. + var appImageCmd = JPackageCommand.helloAppImage().setFakeRuntime(); + appImageCmd.executeAndAssertImageCreated(); + Files.createDirectory(appImageCmd.appLayout().runtimeHomeDirectory().resolve("bin")); + + final var keychain = SignEnvMock.SingleCertificateKeychain.FOO.keychain(); + + var spec = testSpec() + .noAppDesc() + .addArgs("--mac-app-store", "--mac-sign", "--app-image", appImageCmd.outputBundle().toString()) + .error("error.invalid-app-image-runtime-image-bin-dir", + ApplicationLayout.macAppImage().runtimeHomeDirectory(), appImageCmd.outputBundle()) + .create(); + + TKit.withNewState(() -> { + var script = Script.build() + // Disable the mutation making mocks "run once". + .commandMockBuilderMutator(null) + // Replace "/usr/bin/security" with the mock bound to the keychain mock. + .map(MacSignMockUtils.securityMock(SignEnvMock.VALUE)) + // Don't mock other external commands. + .use(VerbatimCommandMock.INSTANCE) + .createLoop(); + + // Create jpackage tool provider using the /usr/bin/security mock. + var jpackage = JPackageMockUtils.createJPackageToolProvider(OperatingSystem.MACOS, script); + + // Override the default jpackage tool provider with the one using the /usr/bin/security mock. + JPackageCommand.useToolProviderByDefault(jpackage); + + spec.test(); + }); + } + @Test(ifOS = MACOS) @ParameterSupplier @ParameterSupplier("testMacPkgSignWithoutIdentity") public static void testMacSignWithoutIdentity(TestSpec spec) { - // The test called JPackage Command.useToolProviderBy Default(), + // The test calls JPackageCommand.useToolProviderByDefault(), // which alters global variables in the test library, // so run the test case with a new global state to isolate the alteration of the globals. TKit.withNewState(() -> { @@ -715,7 +751,7 @@ public final class ErrorTest { private static void testMacSignWithoutIdentityWithNewTKitState(TestSpec spec) { final Token keychainToken = spec.expectedMessages().stream().flatMap(cannedStr -> { - return Stream.of(cannedStr.args()).filter(Token.class::isInstance).map(Token.class::cast).filter(token -> { + return cannedStr.args().stream().filter(Token.class::isInstance).map(Token.class::cast).filter(token -> { switch (token) { case EMPTY_KEYCHAIN, KEYCHAIN_WITH_APP_IMAGE_CERT, KEYCHAIN_WITH_PKG_CERT -> { return true; @@ -1000,8 +1036,7 @@ public final class ErrorTest { // Test a few app-image options that should not be used when signing external app image testCases.addAll(Stream.of( new ArgumentGroup("--app-version", "2.0"), - new ArgumentGroup("--name", "foo"), - new ArgumentGroup("--mac-app-store") + new ArgumentGroup("--name", "foo") ).flatMap(argGroup -> { var withoutSign = testSpec() .noAppDesc() diff --git a/test/jdk/tools/jpackage/share/RuntimePackageTest.java b/test/jdk/tools/jpackage/share/RuntimePackageTest.java index 6cc668f94f9..87fd3496ba5 100644 --- a/test/jdk/tools/jpackage/share/RuntimePackageTest.java +++ b/test/jdk/tools/jpackage/share/RuntimePackageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -23,6 +23,7 @@ import static jdk.internal.util.OperatingSystem.LINUX; import static jdk.internal.util.OperatingSystem.MACOS; +import static jdk.jpackage.test.JPackageCommand.DEFAULT_VERSION; import static jdk.jpackage.test.TKit.assertFalse; import static jdk.jpackage.test.TKit.assertTrue; @@ -81,12 +82,26 @@ public class RuntimePackageTest { @Test public static void test() { - init().run(); + init() + .addInitializer(cmd -> { + // JDK-8357404 enables jpackage to pick a version from the "release" file + // of the predefined runtime when bundling the runtime package. + // This makes the output of this test dependent on the version of the running JDK + // and will be an inconvenience for SQE testing. + // Explicitly specify the package version to fulfill expectations of SQE. + cmd.setArgumentValue("--app-version", DEFAULT_VERSION); + }) + .run(); } @Test(ifOS = MACOS) public static void testFromBundle() { - init(MacHelper::createRuntimeBundle).run(); + init(() -> { + return MacHelper.buildRuntimeBundle().mutator(cmd -> { + // Set custom version in the Info.plist file of the predefined runtime bundle. + cmd.addArguments("--app-version", "17.52"); + }).create(); + }).run(); } @Test(ifOS = LINUX) diff --git a/test/jdk/tools/jpackage/windows/WinInstallerUiTest.java b/test/jdk/tools/jpackage/windows/WinInstallerUiTest.java index d6cebde6444..e9238c1a6f7 100644 --- a/test/jdk/tools/jpackage/windows/WinInstallerUiTest.java +++ b/test/jdk/tools/jpackage/windows/WinInstallerUiTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -21,114 +21,299 @@ * questions. */ +import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.Set; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; import jdk.jpackage.internal.util.Slot; -import jdk.jpackage.test.Annotations.Parameters; +import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.MsiDatabase; +import jdk.jpackage.test.MsiDatabase.UIAlterations; +import jdk.jpackage.test.MsiDatabase.ControlEvent; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.RunnablePackageTest.Action; import jdk.jpackage.test.TKit; +import jdk.jpackage.test.WindowsHelper; /** - * Test all possible combinations of --win-dir-chooser, --win-shortcut-prompt + * Test combinations of --win-dir-chooser, --win-shortcut-prompt, --win-with-ui, * and --license parameters. */ /* * @test - * @summary jpackage with --win-dir-chooser, --win-shortcut-prompt and --license parameters + * @summary jpackage with --win-dir-chooser, --win-shortcut-prompt, --with-with-ui and --license parameters * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @build WinInstallerUiTest * @requires (os.family == "windows") + * @requires (jpackage.test.SQETest != null) + * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=WinInstallerUiTest + * --jpt-exclude=(dir_chooser) + * --jpt-exclude=(license) + * --jpt-exclude=+ui + */ + +/* + * @test + * @summary jpackage with --win-dir-chooser, --win-shortcut-prompt, --with-with-ui and --license parameters + * @library /test/jdk/tools/jpackage/helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @build WinInstallerUiTest + * @requires (os.family == "windows") + * @requires (jpackage.test.SQETest == null) * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=WinInstallerUiTest */ + public class WinInstallerUiTest { - public WinInstallerUiTest(Boolean withDirChooser, Boolean withLicense, - Boolean withShortcutPrompt) { - this.withShortcutPrompt = withShortcutPrompt; - this.withDirChooser = withDirChooser; - this.withLicense = withLicense; + @Test + @ParameterSupplier + public void test(TestSpec spec) { + spec.run(); } - @Parameters - public static List data() { - List data = new ArrayList<>(); - for (var withDirChooser : List.of(Boolean.TRUE, Boolean.FALSE)) { - for (var withLicense : List.of(Boolean.TRUE, Boolean.FALSE)) { - for (var withShortcutPrompt : List.of(Boolean.TRUE, Boolean.FALSE)) { + public static void updateExpectedMsiTables() { + for (var spec : testCases()) { + spec.createTest(true).addBundleVerifier(cmd -> { + spec.save(WindowsHelper.getUIAlterations(cmd)); + }).run(Action.CREATE); + } + } + + record TestSpec( + boolean withDirChooser, + boolean withLicense, + boolean withShortcutPrompt, + boolean withUi) { + + TestSpec { + if (!withDirChooser && !withLicense && !withShortcutPrompt && !withUi) { + throw new IllegalArgumentException(); + } + } + + @Override + public String toString() { + var tokens = new ArrayList(); + if (withDirChooser) { + tokens.add("dir_chooser"); + } + + if (withShortcutPrompt) { + tokens.add("shortcut_prompt"); + } + + if (withLicense) { + tokens.add("license"); + } + + if (withUi) { + tokens.add("ui"); + } + + return tokens.stream().sorted().collect(Collectors.joining("+")); + } + + TestSpec copyWithUi(boolean withUi) { + return new TestSpec(withDirChooser, withLicense, withShortcutPrompt, withUi); + } + + TestSpec copyWithUi() { + return copyWithUi(true); + } + + TestSpec copyWithoutUi() { + return copyWithUi(false); + } + + void run() { + createTest(false).forTypes(PackageType.WIN_MSI).addBundleVerifier(cmd -> { + var expectedFilesDir = expectedFilesDir(); + + var expectedInstallUISequence = Files.readAllLines(expectedFilesDir.resolve(INSTALL_UI_SEQUENCE_FILE)); + var expectedControlEvents = Files.readAllLines(expectedFilesDir.resolve(CONTROL_EVENTS_FILE)); + + var uiAlterations = WindowsHelper.getUIAlterations(cmd); + + var actualInstallUISequence = actionSequenceToMarkdownTable(uiAlterations.installUISequence()); + var actualControlEvents = controlEventsToMarkdownTable(uiAlterations.controlEvents()); + + TKit.assertStringListEquals(expectedInstallUISequence, actualInstallUISequence, + String.format("Check alterations to the `InstallUISequence` MSI table match the contents of [%s] file", + expectedFilesDir.resolve(INSTALL_UI_SEQUENCE_FILE))); + + TKit.assertStringListEquals(expectedControlEvents, actualControlEvents, + String.format("Check alterations to the `ControlEvents` MSI table match the contents of [%s] file", + expectedFilesDir.resolve(CONTROL_EVENTS_FILE))); + }).run(); + } + + PackageTest createTest(boolean onlyMsi) { + return new PackageTest() + .forTypes(onlyMsi ? Set.of(PackageType.WIN_MSI) : PackageType.WINDOWS) + .configureHelloApp() + .addInitializer(JPackageCommand::setFakeRuntime) + .addInitializer(this::setPackageName) + .mutate(test -> { + if (withDirChooser) { + test.addInitializer(cmd -> cmd.addArgument("--win-dir-chooser")); + } + + if (withShortcutPrompt) { + test.addInitializer(cmd -> { + cmd.addArgument("--win-shortcut-prompt"); + cmd.addArgument("--win-menu"); + cmd.addArgument("--win-shortcut"); + }); + } + + if (withLicense) { + setLicenseFile(test); + } + + if (withUi) { + test.addInitializer(cmd -> cmd.addArgument("--win-with-ui")); + } + }); + } + + private void setPackageName(JPackageCommand cmd) { + StringBuilder sb = new StringBuilder(cmd.name()); + sb.append("With"); + if (withDirChooser) { + sb.append("Dc"); // DirChooser + } + if (withShortcutPrompt) { + sb.append("Sp"); // ShortcutPrompt + } + if (withLicense) { + sb.append("L"); // License + } + if (withUi) { + sb.append("Ui"); // UI + } + cmd.setArgumentValue("--name", sb.toString()); + } + + void save(UIAlterations uiAlterations) { + var expectedFilesDir = expectedFilesDir(); + + write(expectedFilesDir.resolve(INSTALL_UI_SEQUENCE_FILE), + actionSequenceToMarkdownTable(uiAlterations.installUISequence())); + + write(expectedFilesDir.resolve(CONTROL_EVENTS_FILE), + controlEventsToMarkdownTable(uiAlterations.controlEvents())); + } + + private Path expectedFilesDir() { + if ((withDirChooser || withShortcutPrompt || withLicense) && withUi) { + return copyWithoutUi().expectedFilesDir(); + } else { + return EXPECTED_MSI_TABLES_ROOT.resolve(toString()); + } + } + + private void write(Path file, List lines) { + try { + Files.createDirectories(file.getParent()); + Files.write(file, lines, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + private static List toMarkdownTable(List header, Stream content) { + return Stream.of( + Stream.of(header.toArray(String[]::new)), + Stream.of(Collections.nCopies(header.size(), "---").toArray(String[]::new)), + content + ).flatMap(x -> x).map(row -> { + return Stream.of(row).map(v -> { + // Escape the pipe (|) character. + return v.replaceAll(Pattern.quote("|"), "|"); + }).collect(Collectors.joining(" | ", "| ", " |")); + }).toList(); + } + + private static List actionSequenceToMarkdownTable(Collection actions) { + return toMarkdownTable( + List.of("Action", "Condition"), + actions.stream().map(action -> { + return toStringArray(action.action(), action.condition()); + }) + ); + } + + private static List controlEventsToMarkdownTable(Collection controlEvents) { + return toMarkdownTable( + List.of("Dialog", "Control", "Event", "Argument", "Condition", "Ordering"), + controlEvents.stream().map(controlEvent -> { + return toStringArray( + controlEvent.dialog(), + controlEvent.control(), + controlEvent.event(), + controlEvent.argument(), + controlEvent.condition(), + Integer.toString(controlEvent.ordering())); + }) + ); + } + + private static String[] toStringArray(String... items) { + return items; + } + + private static final String CONTROL_EVENTS_FILE = "ControlEvents.md"; + private static final String INSTALL_UI_SEQUENCE_FILE = "InstallUISequence.md"; + } + + public static Collection test() { + return Stream.concat( + testCases().stream().filter(Predicate.not(TestSpec::withUi)).map(TestSpec::copyWithUi), + testCases().stream() + ).map(v -> { + return new Object[] {v}; + }).toList(); + } + + private static Collection testCases() { + var testCases = new ArrayList(); + + for (var withDirChooser : List.of(true, false)) { + for (var withLicense : List.of(true, false)) { + for (var withShortcutPrompt : List.of(true, false)) { if (!withDirChooser && !withLicense && !withShortcutPrompt) { // Duplicates SimplePackageTest continue; } - if (withDirChooser && !withLicense && !withShortcutPrompt) { - // Duplicates WinDirChooserTest - continue; - } - - if (!withDirChooser && withLicense && !withShortcutPrompt) { - // Duplicates LicenseTest - continue; - } - - data.add(new Object[]{withDirChooser, withLicense, - withShortcutPrompt}); + testCases.add(new TestSpec(withDirChooser, withLicense, withShortcutPrompt, false)); } } } - return data; - } + // Enforce UI + testCases.add(new TestSpec(false, false, false, true)); - @Test - public void test() { - PackageTest test = new PackageTest() - .forTypes(PackageType.WINDOWS) - .configureHelloApp(); - - test.addInitializer(JPackageCommand::setFakeRuntime); - test.addInitializer(this::setPackageName); - - if (withDirChooser) { - test.addInitializer(cmd -> cmd.addArgument("--win-dir-chooser")); - } - - if (withShortcutPrompt) { - test.addInitializer(cmd -> { - cmd.addArgument("--win-shortcut-prompt"); - cmd.addArgument("--win-menu"); - cmd.addArgument("--win-shortcut"); - }); - } - - if (withLicense) { - setLicenseFile(test); - } - - test.run(); - } - - private void setPackageName(JPackageCommand cmd) { - StringBuilder sb = new StringBuilder(cmd.name()); - sb.append("With"); - if (withDirChooser) { - sb.append("Dc"); // DirChooser - } - if (withShortcutPrompt) { - sb.append("Sp"); // ShortcutPrompt - } - if (withLicense) { - sb.append("L"); // License - } - cmd.setArgumentValue("--name", sb.toString()); + return testCases; } private static void setLicenseFile(PackageTest test) { @@ -143,9 +328,8 @@ public class WinInstallerUiTest { }); } - private final boolean withDirChooser; - private final boolean withLicense; - private final boolean withShortcutPrompt; - private static final Path LICENSE_FILE = TKit.TEST_SRC_ROOT.resolve(Path.of("resources", "license.txt")); + + private static final Path EXPECTED_MSI_TABLES_ROOT = TKit.TEST_SRC_ROOT.resolve( + Path.of("resources", WinInstallerUiTest.class.getSimpleName())); } diff --git a/test/jdk/tools/jpackage/windows/WinL10nTest.java b/test/jdk/tools/jpackage/windows/WinL10nTest.java index 14139013318..4d983df2598 100644 --- a/test/jdk/tools/jpackage/windows/WinL10nTest.java +++ b/test/jdk/tools/jpackage/windows/WinL10nTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -21,22 +21,22 @@ * questions. */ +import static jdk.jpackage.test.WindowsHelper.getWixTypeFromVerboseJPackageOutput; +import static jdk.jpackage.test.WindowsHelper.WixType.WIX3; + import java.io.IOException; import java.nio.file.Path; -import jdk.jpackage.test.TKit; -import jdk.jpackage.test.PackageTest; -import jdk.jpackage.test.PackageType; -import jdk.jpackage.test.Annotations.Test; -import jdk.jpackage.test.Annotations.Parameters; - import java.util.Arrays; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; +import jdk.jpackage.test.Annotations.Parameters; +import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.Executor; -import static jdk.jpackage.test.WindowsHelper.WixType.WIX3; -import static jdk.jpackage.test.WindowsHelper.getWixTypeFromVerboseJPackageOutput; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.TKit; /* * @test @@ -124,8 +124,17 @@ public class WinL10nTest { var toolFileName = wixToolName + ".exe"; return (s) -> { s = s.trim(); - return s.startsWith(toolFileName) || ((s.contains(String.format("\\%s ", toolFileName)) && s. - contains(" -out "))); + + if (s.startsWith(toolFileName)) { + return true; + } + + // Accommodate for: + // 'C:\Program Files (x86)\WiX Toolset v3.14\bin\light.exe' ... + // light.exe ... + return Stream.of("\\%s ", "\\%s' ").map(format -> { + return String.format(format, toolFileName); + }).anyMatch(s::contains) && s.contains(" -out "); }; } diff --git a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java index 70e619f3d3d..2d604a92b75 100644 --- a/test/jtreg-ext/requires/VMProps.java +++ b/test/jtreg-ext/requires/VMProps.java @@ -107,7 +107,6 @@ public class VMProps implements Callable> { map.put("vm.debug", this::vmDebug); map.put("vm.jvmci", this::vmJvmci); map.put("vm.jvmci.enabled", this::vmJvmciEnabled); - map.put("vm.emulatedClient", this::vmEmulatedClient); // vm.hasSA is "true" if the VM contains the serviceability agent // and jhsdb. map.put("vm.hasSA", this::vmHasSA); @@ -299,18 +298,6 @@ public class VMProps implements Callable> { return "" + Compiler.isJVMCIEnabled(); } - - /** - * @return true if VM runs in emulated-client mode and false otherwise. - */ - protected String vmEmulatedClient() { - String vmInfo = System.getProperty("java.vm.info"); - if (vmInfo == null) { - return errorWithMessage("Can't get 'java.vm.info' property"); - } - return "" + vmInfo.contains(" emulated-client"); - } - /** * @return supported CPU features */ diff --git a/test/langtools/jdk/internal/shellsupport/doc/FullJavadocHelperTest.java b/test/langtools/jdk/internal/shellsupport/doc/FullJavadocHelperTest.java index fcfd40b3292..5219b8e9080 100644 --- a/test/langtools/jdk/internal/shellsupport/doc/FullJavadocHelperTest.java +++ b/test/langtools/jdk/internal/shellsupport/doc/FullJavadocHelperTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -30,19 +30,19 @@ * jdk.compiler/com.sun.tools.javac.main * jdk.jshell/jdk.internal.shellsupport.doc * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask - * @run testng/timeout=900/othervm -Xmx1024m FullJavadocHelperTest + * @run junit/timeout=900/othervm -Xmx1024m FullJavadocHelperTest */ import java.io.IOException; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; -@Test public class FullJavadocHelperTest { /* * Long-running test to retrieve doc comments for enclosed elements of all JDK classes. */ + @Test public void testAllDocs() throws IOException { new JavadocHelperTest().retrieveDocComments(Boolean.TRUE::booleanValue); } diff --git a/test/langtools/jdk/internal/shellsupport/doc/JavadocFormatterTest.java b/test/langtools/jdk/internal/shellsupport/doc/JavadocFormatterTest.java index 153c010c33d..2ee326e26f9 100644 --- a/test/langtools/jdk/internal/shellsupport/doc/JavadocFormatterTest.java +++ b/test/langtools/jdk/internal/shellsupport/doc/JavadocFormatterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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,21 +27,21 @@ * @summary Test JavadocFormatter * @library /tools/lib * @modules jdk.jshell/jdk.internal.shellsupport.doc - * @run testng JavadocFormatterTest + * @run junit JavadocFormatterTest */ import java.util.Objects; import jdk.internal.shellsupport.doc.JavadocFormatter; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; -@Test public class JavadocFormatterTest { private static final String CODE_RESET = "\033[0m"; private static final String CODE_HIGHLIGHT = "\033[1m"; private static final String CODE_UNDERLINE = "\033[4m"; + @Test public void testReflow() { String actual; String expected; @@ -401,6 +401,7 @@ public class JavadocFormatterTest { } } + @Test public void testSpaceAtEndOfLine() { String header = "Class Class.forName(Module module, String name)"; String javadoc = """ diff --git a/test/langtools/jdk/internal/shellsupport/doc/JavadocHelperTest.java b/test/langtools/jdk/internal/shellsupport/doc/JavadocHelperTest.java index e82553a4cfe..4f739d38d3d 100644 --- a/test/langtools/jdk/internal/shellsupport/doc/JavadocHelperTest.java +++ b/test/langtools/jdk/internal/shellsupport/doc/JavadocHelperTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -30,7 +30,7 @@ * jdk.compiler/com.sun.tools.javac.main * jdk.jshell/jdk.internal.shellsupport.doc * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask - * @run testng JavadocHelperTest + * @run junit JavadocHelperTest * @key randomness */ @@ -69,13 +69,13 @@ import javax.tools.ToolProvider; import com.sun.source.util.JavacTask; import jdk.internal.shellsupport.doc.JavadocHelper; -import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; -@Test public class JavadocHelperTest { + @Test public void testJavadoc() throws Exception { doTestJavadoc("", t -> t.getElements().getTypeElement("test.Super"), @@ -93,6 +93,7 @@ public class JavadocHelperTest { " @return value\n"); } + @Test public void testInheritNoJavadoc() throws Exception { doTestJavadoc("", getSubTest, @@ -107,6 +108,7 @@ public class JavadocHelperTest { " @return value\n"); } + @Test public void testInheritFull() throws Exception { doTestJavadoc(" /**\n" + " * Prefix {@inheritDoc} suffix.\n" + @@ -131,6 +133,7 @@ public class JavadocHelperTest { " @return prefix value suffix\n"); } + @Test public void testInheritMissingParam() throws Exception { doTestJavadoc(" /**\n" + " * Prefix {@inheritDoc} suffix.\n" + @@ -154,6 +157,7 @@ public class JavadocHelperTest { " @return prefix value suffix\n"); } + @Test public void testInheritMissingFirstParam() throws Exception { doTestJavadoc(" /**\n" + " * Prefix {@inheritDoc} suffix.\n" + @@ -177,6 +181,7 @@ public class JavadocHelperTest { " @return prefix value suffix\n"); } + @Test public void testInheritMissingThrows() throws Exception { doTestJavadoc(" /**\n" + " * Prefix {@inheritDoc} suffix.\n" + @@ -200,6 +205,7 @@ public class JavadocHelperTest { " @return prefix value suffix\n"); } + @Test public void testInheritMissingReturn() throws Exception { doTestJavadoc(" /**\n" + " * Prefix {@inheritDoc} suffix.\n" + @@ -223,6 +229,7 @@ public class JavadocHelperTest { "@return value\n"); } + @Test public void testInheritAllButOne() throws Exception { doTestJavadoc(" /**\n" + " * @throws IllegalArgumentException {@inheritDoc}\n" + @@ -238,6 +245,7 @@ public class JavadocHelperTest { "@return value\n"); } + @Test public void testInheritEmpty() throws Exception { doTestJavadoc(" /**\n" + " */\n", @@ -260,6 +268,7 @@ public class JavadocHelperTest { "@return \n"); } + @Test public void testEmptyValue() throws Exception { doTestJavadoc(" /**\n" + " */\n", @@ -282,6 +291,7 @@ public class JavadocHelperTest { "@return \n"); } + @Test public void testShortComment() throws Exception { doTestJavadoc(" /**Test.*/\n", getSubTest, @@ -295,6 +305,7 @@ public class JavadocHelperTest { "@return value\n"); } + @Test public void testMarkdown() throws Exception { doTestJavadoc(""" /// Prefix {@inheritDoc} suffix. @@ -328,6 +339,7 @@ public class JavadocHelperTest { @return prefix value suffix"""); } + @Test public void testMarkdown2() throws Exception { doTestJavadoc(""" /// {@inheritDoc} @@ -352,6 +364,7 @@ public class JavadocHelperTest { @since snc"""); } + @Test public void testMarkdown3() throws Exception { doTestJavadoc(""" /// {@inheritDoc} @@ -373,6 +386,7 @@ public class JavadocHelperTest { """); } + @Test public void testMarkdown4() throws Exception { doTestJavadoc(""" /// {@inheritDoc} @@ -397,6 +411,7 @@ public class JavadocHelperTest { @since snc"""); } + @Test public void testMarkdown5() throws Exception { doTestJavadoc(""" ///[define classes][java.lang.invoke.MethodHandles.Lookup#defineClass(byte\\[\\])] @@ -417,6 +432,7 @@ public class JavadocHelperTest { @since snc"""); } + @Test public void testMarkdown6() throws Exception { doTestJavadoc(""" ///Text1 [define classes][java.lang.invoke.MethodHandles.Lookup#defineClass(byte\\[\\])] @@ -503,7 +519,7 @@ public class JavadocHelperTest { try (JavadocHelper helper = JavadocHelper.create(task, Arrays.asList(srcZip))) { String javadoc = helper.getResolvedDocComment(el); - assertEquals(javadoc, expectedJavadoc); + assertEquals(expectedJavadoc, javadoc); } } } @@ -547,6 +563,7 @@ public class JavadocHelperTest { * Set the system property `seed` to a random seed to reproduce * a specific run of this test. */ + @Test public void testRandomDocs() throws IOException { Random random = new Random(getSeed()); // Run test on 2% of classes, which corresponds to ~ 140 classes diff --git a/test/langtools/jdk/jshell/ConcurrentHistoryLoadingTest.java b/test/langtools/jdk/jshell/ConcurrentHistoryLoadingTest.java new file mode 100644 index 00000000000..926dbbf50b4 --- /dev/null +++ b/test/langtools/jdk/jshell/ConcurrentHistoryLoadingTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2026, 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.io.ByteArrayInputStream; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.prefs.Preferences; + +import jdk.jshell.tool.JavaShellToolBuilder; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/* + * @test + * @bug 8347418 + * @summary Verify that loading of JShell history doesn't lead to a + * NullPointerException when the Preferences are modified concurrently. + * @run junit ConcurrentHistoryLoadingTest + */ +public class ConcurrentHistoryLoadingTest { + + private static final String HISTORY_LINE_PREFIX = "HISTORY_LINE_"; + + @Test + public void testConcurrentHistoryLoading() throws Throwable { + AtomicBoolean removeOnAccess = new AtomicBoolean(); + Preferences testPrefs = new ReplToolTesting.MemoryPreferences() { + @Override + protected String getSpi(String key) { + String result = super.getSpi(key); + if (key.startsWith(HISTORY_LINE_PREFIX) && removeOnAccess.getAndSet(false)) { + for (String key2Remote : keysSpi()) { + remove(key2Remote); + } + } + return result; + } + }; + StringBuilder input = new StringBuilder(); + int max = 10; + for (int j = 0; j < max; j++) { + input.append("int x").append(j).append(" = 42\n"); + } + JavaShellToolBuilder + .builder() + .persistence(testPrefs) + .in(new ByteArrayInputStream(input.toString().getBytes()), null) + .start(); + Assertions.assertEquals(10, Arrays.stream(testPrefs.keys()) + .filter(key -> key.startsWith(HISTORY_LINE_PREFIX)) + .count()); + removeOnAccess.set(true); + JavaShellToolBuilder + .builder() + .persistence(testPrefs) + .in(new ByteArrayInputStream(input.toString().getBytes()), null) + .start(); + + } +} diff --git a/test/langtools/jdk/jshell/InputUITest.java b/test/langtools/jdk/jshell/InputUITest.java index 1a420d2c345..6886e1302a2 100644 --- a/test/langtools/jdk/jshell/InputUITest.java +++ b/test/langtools/jdk/jshell/InputUITest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -23,7 +23,7 @@ /* * @test - * @bug 8356165 8358552 + * @bug 8356165 8358552 8378251 * @summary Check user input works properly * @modules * jdk.compiler/com.sun.tools.javac.api @@ -99,4 +99,34 @@ public class InputUITest extends UITesting { waitOutput(out, patternQuote("==> 65")); }, false); } -} \ No newline at end of file + + @Test + public void testAltBackspaceDeletesPreviousWord() throws Exception { + doRunTest((inputSink, out) -> { + inputSink.write("int x = 12 24" + ESC_DEL + "\n"); + waitOutput(out, "int x = 12 24\u001B\\[2D\u001B\\[K\n" + + "\u001B\\[\\?2004lx ==> 12\n" + + "\u001B\\[\\?2004h" + PROMPT); + inputSink.write("System.in" + ESC_DEL + "out.println(x)\n"); + waitOutput(out, "System.in\u001B\\[2D\u001B\\[Kout.println\\(x\\)\u001B\\[3D\u001B\\[3C\n" + + "\u001B\\[\\?2004l12\n" + + "\u001B\\[\\?2004h" + PROMPT); + }, false); + } + + @Test + public void testAltDDeletesNextWord() throws Exception { + doRunTest((inputSink, out) -> { + inputSink.write("int x = 12 24" + ESC_B + ESC_D + "\n"); + waitOutput(out, "int x = 12 24\u001B\\[2D\u001B\\[K\n" + + "\u001B\\[\\?2004lx ==> 12\n" + + "\u001B\\[\\?2004h" + PROMPT); + inputSink.write("System.in.println" + ESC_B + ESC_B + ESC_D + + "out" + ESC_F + ESC_F + "(x)\n"); + waitOutput(out, "System.in.println\u001B\\[7D\u001B\\[3D\u001B\\[2P" + + "\u001B\\[1@o\u001B\\[1@u\u001B\\[1@t\u001B\\[C\u001B\\[7C\\(x\\)\u001B\\[3D\u001B\\[3C\n" + + "\u001B\\[\\?2004l12\n" + + "\u001B\\[\\?2004h" + PROMPT); + }, false); + } +} diff --git a/test/langtools/jdk/jshell/ReplToolTesting.java b/test/langtools/jdk/jshell/ReplToolTesting.java index 429a0a7ce02..2dabf29e1f9 100644 --- a/test/langtools/jdk/jshell/ReplToolTesting.java +++ b/test/langtools/jdk/jshell/ReplToolTesting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -914,7 +914,7 @@ public class ReplToolTesting { } } - public static final class MemoryPreferences extends AbstractPreferences { + public static class MemoryPreferences extends AbstractPreferences { private final Map values = new HashMap<>(); private final Map nodes = new HashMap<>(); @@ -943,17 +943,17 @@ public class ReplToolTesting { } @Override - protected void removeNodeSpi() throws BackingStoreException { + protected void removeNodeSpi() { ((MemoryPreferences) parent()).nodes.remove(name()); } @Override - protected String[] keysSpi() throws BackingStoreException { + protected String[] keysSpi() { return values.keySet().toArray(new String[0]); } @Override - protected String[] childrenNamesSpi() throws BackingStoreException { + protected String[] childrenNamesSpi() { return nodes.keySet().toArray(new String[0]); } @@ -963,11 +963,11 @@ public class ReplToolTesting { } @Override - protected void syncSpi() throws BackingStoreException { + protected void syncSpi() { } @Override - protected void flushSpi() throws BackingStoreException { + protected void flushSpi() { } } diff --git a/test/langtools/jdk/jshell/TerminalNoExecTest.java b/test/langtools/jdk/jshell/TerminalNoExecTest.java index 3d76157fd26..d7cd20046af 100644 --- a/test/langtools/jdk/jshell/TerminalNoExecTest.java +++ b/test/langtools/jdk/jshell/TerminalNoExecTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -39,7 +39,7 @@ import java.io.Writer; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.concurrent.TimeUnit; +import java.util.HashMap; import java.util.concurrent.atomic.AtomicBoolean; import jdk.jfr.consumer.RecordingStream; import jdk.jshell.tool.JavaShellToolBuilder; @@ -58,7 +58,9 @@ public class TerminalNoExecTest { spawnedNewProcess.set(true); }); rs.startAsync(); - JavaShellToolBuilder.builder().run("--execution=local", "--no-startup"); + JavaShellToolBuilder.builder() + .persistence(new HashMap<>()) + .run("--execution=local", "--no-startup"); rs.stop(); } if (spawnedNewProcess.get()) { diff --git a/test/langtools/jdk/jshell/UITesting.java b/test/langtools/jdk/jshell/UITesting.java index a1bd8f35dee..d63a8460889 100644 --- a/test/langtools/jdk/jshell/UITesting.java +++ b/test/langtools/jdk/jshell/UITesting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -53,6 +53,10 @@ public class UITesting { protected static final String UP = "\033[A"; protected static final String DOWN = "\033[B"; protected static final String CTRL_D = "\u0004"; + protected static final String ESC_DEL = "\u001B\u007F"; // ESC + DEL (common Alt+Backspace) + protected static final String ESC_B = "\u001Bb"; // ESC + b (common Alt+b) + protected static final String ESC_F = "\u001Bf"; // ESC + f (common Alt+f) + protected static final String ESC_D = "\u001Bd"; // ESC + d (common Alt+d) private final boolean laxLineEndings; public UITesting() { diff --git a/test/langtools/lib/combo/tools/javac/combo/CompilationTestCase.java b/test/langtools/lib/combo/tools/javac/combo/CompilationTestCase.java index 0760352e23b..c3aa92d1f37 100644 --- a/test/langtools/lib/combo/tools/javac/combo/CompilationTestCase.java +++ b/test/langtools/lib/combo/tools/javac/combo/CompilationTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -112,6 +112,10 @@ public class CompilationTestCase extends JavacTemplateTestBase { assertCompile(expandMarkers(constructs), () -> assertCompileSucceededWithWarning(warning), false); } + protected void assertOKWithWarning(String warning, int numberOfTimes, String... constructs) { + assertCompile(expandMarkers(constructs), () -> assertCompileSucceededWithWarning(warning, numberOfTimes), false); + } + protected void assertFail(String expectedDiag, String... constructs) { assertCompile(expandMarkers(constructs), () -> assertCompileFailed(expectedDiag), false); } diff --git a/test/langtools/lib/combo/tools/javac/combo/Diagnostics.java b/test/langtools/lib/combo/tools/javac/combo/Diagnostics.java index 47f496b5891..87ee54b375a 100644 --- a/test/langtools/lib/combo/tools/javac/combo/Diagnostics.java +++ b/test/langtools/lib/combo/tools/javac/combo/Diagnostics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, 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 @@ -84,6 +84,12 @@ public class Diagnostics implements javax.tools.DiagnosticListener d.getCode().equals(key)); } + public boolean containsWarningKey(String key, int numberOfWarnings) { + return diags.stream() + .filter(d -> d.getKind() == Diagnostic.Kind.WARNING || d.getKind() == Diagnostic.Kind.MANDATORY_WARNING) + .filter(d -> d.getCode().equals(key)).count() == numberOfWarnings; + } + /** Get the error keys */ public List errorKeys() { return diags.stream() diff --git a/test/langtools/lib/combo/tools/javac/combo/JavacTemplateTestBase.java b/test/langtools/lib/combo/tools/javac/combo/JavacTemplateTestBase.java index ed5936210a8..3b4b67e7968 100644 --- a/test/langtools/lib/combo/tools/javac/combo/JavacTemplateTestBase.java +++ b/test/langtools/lib/combo/tools/javac/combo/JavacTemplateTestBase.java @@ -154,6 +154,14 @@ public abstract class JavacTemplateTestBase { } } + protected void assertCompileSucceededWithWarning(String warning, int numberOfWarnings) { + if (diags.errorsFound()) + fail("Expected successful compilation"); + if (!diags.containsWarningKey(warning, numberOfWarnings)) { + fail(String.format("Expected compilation warning with %s, found %s", warning, diags.keys())); + } + } + /** * If the provided boolean is true, assert all previous compiles succeeded, * otherwise assert that a compile failed. diff --git a/test/langtools/tools/javac/annotations/6365854/test1.out b/test/langtools/tools/javac/annotations/6365854/test1.out index c8bf69b095d..e69de29bb2d 100644 --- a/test/langtools/tools/javac/annotations/6365854/test1.out +++ b/test/langtools/tools/javac/annotations/6365854/test1.out @@ -1,2 +0,0 @@ -TestCore.class:-:-: compiler.warn.annotation.method.not.found.reason: test.annotation.TestAnnotation, test, (compiler.misc.class.file.not.found: test.annotation.TestAnnotation) -1 warning diff --git a/test/langtools/tools/javac/annotations/crashOnUnknownAttr/CrashOnUnknownTargetTypeTest.java b/test/langtools/tools/javac/annotations/crashOnUnknownAttr/CrashOnUnknownTargetTypeTest.java index 7f5d49e38c9..d0c19c3ec09 100644 --- a/test/langtools/tools/javac/annotations/crashOnUnknownAttr/CrashOnUnknownTargetTypeTest.java +++ b/test/langtools/tools/javac/annotations/crashOnUnknownAttr/CrashOnUnknownTargetTypeTest.java @@ -3,7 +3,7 @@ * @summary compiler is crashing with AssertionError for annotations with unknown target type * @bug 8296010 * @build A - * @compile/fail/ref=CrashOnUnknownTargetTypeTest.out -XDrawDiagnostics CrashOnUnknownTargetTypeTest.java + * @compile/fail/ref=CrashOnUnknownTargetTypeTest.out -XDrawDiagnostics -Xlint:classfile CrashOnUnknownTargetTypeTest.java */ public class CrashOnUnknownTargetTypeTest { diff --git a/test/langtools/tools/javac/annotations/crashOnUnknownAttr/CrashOnUnknownTargetTypeTest.out b/test/langtools/tools/javac/annotations/crashOnUnknownAttr/CrashOnUnknownTargetTypeTest.out index 8e6925a0e0d..d46ea56acef 100644 --- a/test/langtools/tools/javac/annotations/crashOnUnknownAttr/CrashOnUnknownTargetTypeTest.out +++ b/test/langtools/tools/javac/annotations/crashOnUnknownAttr/CrashOnUnknownTargetTypeTest.out @@ -1,5 +1,5 @@ -- compiler.warn.unknown.enum.constant: ElementType.class, java.lang.annotation.ElementType, NO_SUCH -- compiler.warn.unknown.enum.constant: String.class, java.lang.annotation.ElementType, NO_SUCH +A.class:-:-: compiler.warn.unknown.enum.constant: ElementType.class, java.lang.annotation.ElementType, NO_SUCH +A.class:-:-: compiler.warn.unknown.enum.constant: String.class, java.lang.annotation.ElementType, NO_SUCH CrashOnUnknownTargetTypeTest.java:10:5: compiler.err.annotation.unrecognized.attribute.name: A, NO_SUCH CrashOnUnknownTargetTypeTest.java:11:5: compiler.err.annotation.unrecognized.attribute.name: A, NO_SUCH CrashOnUnknownTargetTypeTest.java:12:14: compiler.err.annotation.unrecognized.attribute.name: A, NO_SUCH diff --git a/test/langtools/tools/javac/annotations/repeatingAnnotations/generatedInRepeating/ComplexGeneratedInRepeating.java b/test/langtools/tools/javac/annotations/repeatingAnnotations/generatedInRepeating/ComplexGeneratedInRepeating.java new file mode 100644 index 00000000000..2ddb15a64ea --- /dev/null +++ b/test/langtools/tools/javac/annotations/repeatingAnnotations/generatedInRepeating/ComplexGeneratedInRepeating.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2026, 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 8378524 + * @summary Check that repeating annotations whose attributes are not-yet-generated classes and their members work. + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit ComplexGeneratedInRepeating + */ + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.element.TypeElement; +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.ToolBox; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +public class ComplexGeneratedInRepeating { + + Path base; + ToolBox tb = new ToolBox(); + + @Test + void testMember() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString()) + .sources(""" + package test; + + import java.lang.annotation.Repeatable; + + @Rep(Constants.C) + @Rep(Constants.C) + public class Test {} + + @Repeatable(Reps.class) + @interface Rep { + int value(); + } + @interface Reps { + Rep[] value(); + } + """) + .processors(new ProcessorImpl()) + .run() + .writeAll(); + } + + @Test + void testUnresolvableMember() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics", "-nowarn") + .sources(""" + package test; + + import java.lang.annotation.Repeatable; + + @Rep(Constants.C) + @Rep(Constants.Unknown) + public class Test {} + + @Repeatable(Reps.class) + @interface Rep { + int value(); + } + @interface Reps { + Rep[] value(); + } + """) + .processors(new ProcessorImpl()) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Test.java:6:15: compiler.err.cant.resolve.location: kindname.variable, Unknown, , , (compiler.misc.location: kindname.class, test.Constants, null)", + "1 error")); + } + + @Test + void testIncompatibleMember() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics", "-nowarn") + .sources(""" + package test; + + import java.lang.annotation.Repeatable; + + @Rep(Constants.C) + @Rep(Constants.S) + public class Test {} + + @Repeatable(Reps.class) + @interface Rep { + int value(); + } + @interface Reps { + Rep[] value(); + } + """) + .processors(new ProcessorImpl()) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Test.java:6:15: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, int)", + "1 error")); + } + + @Test + void testAnnotation() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString()) + .sources(""" + package test; + + import java.lang.annotation.Repeatable; + + @Rep(@Ann(Constants.C)) + @Rep(@Ann(Constants.C)) + public class Test {} + + @Repeatable(Reps.class) + @interface Rep { + Ann value(); + } + @interface Reps { + Rep[] value(); + } + """) + .processors(new ProcessorImpl()) + .run() + .writeAll(); + } + + @SupportedAnnotationTypes("*") + private static class ProcessorImpl extends AbstractProcessor { + + int round = 0; + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (round++ == 0) { + try (Writer w = processingEnv.getFiler().createSourceFile("test.Constants").openWriter()) { + w.append(""" + package test; + public class Constants { + public static final int C = 0; + public static final String S = ""; + } + """); + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + try (Writer w = processingEnv.getFiler().createSourceFile("test.Ann").openWriter()) { + w.append(""" + package test; + public @interface Ann { + int value(); + } + """); + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + return false; + } + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } +} diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/BridgeShouldHaveNoInteriorAnnotationsTest.java b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/BridgeShouldHaveNoInteriorAnnotationsTest.java index 0ec1a790d72..3426bfedd30 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/BridgeShouldHaveNoInteriorAnnotationsTest.java +++ b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/BridgeShouldHaveNoInteriorAnnotationsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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,7 +71,7 @@ public class BridgeShouldHaveNoInteriorAnnotationsTest }; - // Expected output can't be directly encoded into NestedLambdasCastedTest !!! + // Expected output can't be directly encoded into BridgeShouldHaveNoInteriorAnnotationsTest !!! static class OutputExpectedOnceHolder { public String[] outputs = { "0: #120(): CAST, offset=1, type_index=0, location=[TYPE_ARGUMENT(0)]", diff --git a/test/langtools/tools/javac/classreader/Annotations.java b/test/langtools/tools/javac/classreader/Annotations.java new file mode 100644 index 00000000000..30d0ae48b9a --- /dev/null +++ b/test/langtools/tools/javac/classreader/Annotations.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2026, 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 8305250 + * @summary Check behavior w.r.t. annotations missing from the classpath. + * @library /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit Annotations + */ + +import java.nio.file.Files; +import java.util.List; +import java.nio.file.Path; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import toolbox.ToolBox; +import toolbox.JavacTask; +import toolbox.Task; + +public class Annotations { + private ToolBox tb = new ToolBox(); + private Path base; + + @Test + public void testParameterModifiersNotVisible() throws Exception { + Path ann = base.resolve("annotations"); + Path annSrc = ann.resolve("src"); + Path annClasses = ann.resolve("classes"); + + tb.writeJavaFiles(annSrc, + """ + package annotations; + public @interface Ann { + public E e(); + } + """, + """ + package annotations; + public enum E { + A; + } + """); + + Files.createDirectories(annClasses); + + new JavacTask(tb) + .outdir(annClasses) + .files(tb.findJavaFiles(annSrc)) + .run() + .writeAll(); + + Path lib = base.resolve("lib"); + Path libSrc = lib.resolve("src"); + Path libClasses = lib.resolve("classes"); + + tb.writeJavaFiles(libSrc, + """ + package lib; + import annotations.*; + @Ann(e = E.A) + public class Lib { + } + """); + + Files.createDirectories(libClasses); + + new JavacTask(tb) + .outdir(libClasses) + .classpath(annClasses) + .files(tb.findJavaFiles(libSrc)) + .run() + .writeAll(); + + Path test = base.resolve("test"); + Path testSrc = test.resolve("src"); + Path testClasses = test.resolve("classes"); + + tb.writeJavaFiles(testSrc, + """ + package test; + import lib.*; + public class Test { + Lib l; + } + """); + + Files.createDirectories(testClasses); + + //annotations available, no errors/warnings: + new JavacTask(tb) + .outdir(testClasses) + .classpath(libClasses, annClasses) + .options("-Werror", "-Xlint:classfile") + .files(tb.findJavaFiles(testSrc)) + .run() + .writeAll(); + + //annotation and enum missing, no errors/warnings: + new JavacTask(tb) + .outdir(testClasses) + .classpath(libClasses) + .options("-Werror", "-Xlint:classfile") + .files(tb.findJavaFiles(testSrc)) + .run() + .writeAll(); + + tb.writeJavaFiles(annSrc, + """ + package annotations; + public enum E { + B; + } + """); + + Files.createDirectories(annClasses); + + new JavacTask(tb) + .outdir(annClasses) + .files(tb.findJavaFiles(annSrc)) + .run() + .writeAll(); + + List log; + + //enum missing the enum constant recorded in the classfile, report warning: + log = new JavacTask(tb) + .outdir(testClasses) + .classpath(libClasses, annClasses) + .options("-Xlint:classfile", "-XDrawDiagnostics") + .files(tb.findJavaFiles(testSrc)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + tb.checkEqual(log, + List.of("Lib.class:-:-: compiler.warn.unknown.enum.constant: E.class, annotations.E, A", + "1 warning")); + + //enum is missing, but the annotation is not, report warning: + Files.delete(annClasses.resolve("annotations").resolve("E.class")); + + log = new JavacTask(tb) + .outdir(testClasses) + .classpath(libClasses, annClasses) + .options("-Xlint:classfile", "-XDrawDiagnostics") + .files(tb.findJavaFiles(testSrc)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + tb.checkEqual(log, + List.of("Lib.class:-:-: compiler.warn.unknown.enum.constant.reason: Ann.class, annotations.E, A, (compiler.misc.class.file.not.found: annotations.E)", + "1 warning")); + + tb.writeJavaFiles(annSrc, + """ + package annotations; + public @interface Ann { + public E nue(); + } + """, + """ + package annotations; + public enum E { + A; + } + """); + + new JavacTask(tb) + .outdir(annClasses) + .files(tb.findJavaFiles(annSrc)) + .run() + .writeAll(); + + //enum is OK and the annotation exists, but the annotation is missing the required attribute method, report warning: + log = new JavacTask(tb) + .outdir(testClasses) + .classpath(libClasses, annClasses) + .options("-Xlint:classfile", "-XDrawDiagnostics") + .files(tb.findJavaFiles(testSrc)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + tb.checkEqual(log, + List.of("Lib.class:-:-: compiler.warn.annotation.method.not.found: annotations.Ann, e", + "1 warning")); + } + + @BeforeEach + public void setup(TestInfo ti) { + base = Path.of(".").resolve(ti.getTestMethod().orElseThrow().getName()); + } +} diff --git a/test/langtools/tools/javac/diags/CheckExamples.java b/test/langtools/tools/javac/diags/CheckExamples.java index 502f5fa8b88..7a188614080 100644 --- a/test/langtools/tools/javac/diags/CheckExamples.java +++ b/test/langtools/tools/javac/diags/CheckExamples.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2026, 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 @@ -45,12 +45,22 @@ import java.util.*; /** * Check invariants for a set of examples. + * + * READ THIS IF THIS TEST FAILS AFTER ADDING A NEW KEY TO 'compiler.properties': + * The 'examples' subdirectory contains a number of examples which provoke + * the reporting of most of the compiler message keys. + * * -- each example should exactly declare the keys that will be generated when * it is run. + * -- this is done by the "// key:"-comment in each fine. * -- together, the examples should cover the set of resource keys in the * compiler.properties bundle. A list of exceptions may be given in the * not-yet.txt file. Entries on the not-yet.txt list should not be * covered by examples. + * -- some keys are only reported by the compiler when specific options are + * supplied. For the purposes of this test, this can be specified by a + * comment e.g. like this: "// options: -Xlint:empty" + * * When new keys are added to the resource bundle, it is strongly recommended * that corresponding new examples be added here, if at all practical, instead * of simply and lazily being added to the not-yet.txt list. diff --git a/test/langtools/tools/javac/implicitCompile/APImplicitClassesWarnings.java b/test/langtools/tools/javac/implicitCompile/APImplicitClassesWarnings.java new file mode 100644 index 00000000000..dc60c6cc78a --- /dev/null +++ b/test/langtools/tools/javac/implicitCompile/APImplicitClassesWarnings.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2026, 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 8378740 + * @summary Verify warnings are properly suppressed for the combination of + * annotation processing and implicit compilation + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit APImplicitClassesWarnings + */ + + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.TypeElement; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.ToolBox; + +public class APImplicitClassesWarnings { + + final ToolBox tb = new ToolBox(); + Path base; + + @Test + public void testCorrectSource() throws Exception { + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + package test; + + @Deprecated(forRemoval=true) + public class Depr { + } + """, + """ + package test; + public class Use { + Implicit implicit; + Depr depr; + } + """, + """ + package test; + public interface Implicit {} + """); + Files.createDirectories(classes); + + List log = new JavacTask(tb) + .options("-d", classes.toString(), + "-XDrawDiagnostics", + "-implicit:class", + "-sourcepath", src.toString()) + .files(src.resolve("test").resolve("Depr.java"), + src.resolve("test").resolve("Use.java")) + .processors(new ProcessorImpl()) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = List.of( + "Use.java:4:5: compiler.warn.has.been.deprecated.for.removal: test.Depr, test", + "Use.java:4:5: compiler.warn.has.been.deprecated.for.removal: test.Depr, test", + "Use.java:4:5: compiler.warn.has.been.deprecated.for.removal: test.Depr, test", + "3 warnings" + ); + + tb.checkEqual(expected, log); + } + + @Test + public void testCorrectSuppress() throws Exception { + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + //note the added @SuppressWarnings("removal"): + """ + package test; + + @Deprecated(forRemoval=true) + public class Depr { + } + """, + """ + package test; + public class Use { + Implicit implicit; + @SuppressWarnings("removal") + Depr depr; + } + """, + """ + package test; + public interface Implicit {} + """); + Files.createDirectories(classes); + + new JavacTask(tb) + .options("-d", classes.toString(), + "-Werror", + "-implicit:class", + "-sourcepath", src.toString()) + .files(src.resolve("test").resolve("Depr.java"), + src.resolve("test").resolve("Use.java")) + .processors(new ProcessorImpl()) + .run() + .writeAll(); + } + + @SupportedAnnotationTypes("*") + private static class ProcessorImpl extends AbstractProcessor { + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + return false; + } + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod().orElseThrow().getName()); + } +} diff --git a/test/langtools/tools/javac/launcher/SourceLauncherTest.java b/test/langtools/tools/javac/launcher/SourceLauncherTest.java index c3c8fb19a1b..d0cf11a0327 100644 --- a/test/langtools/tools/javac/launcher/SourceLauncherTest.java +++ b/test/langtools/tools/javac/launcher/SourceLauncherTest.java @@ -670,6 +670,7 @@ public class SourceLauncherTest extends TestRunner { tb.writeJavaFiles(base, "public class Main { public static void main(String... args) {}}"); String log = new JavaTask(tb) .vmOptions("--source", "21") + .includeStandardOptions(false) // Do not inherit --enable-preview .className(base.resolve("Main.java").toString()) .run(Task.Expect.SUCCESS) .getOutput(Task.OutputKind.STDERR); diff --git a/test/langtools/tools/javac/patterns/BreakAndLoops.java b/test/langtools/tools/javac/patterns/BreakAndLoops.java index 31ad8908f53..777d5ae4f42 100644 --- a/test/langtools/tools/javac/patterns/BreakAndLoops.java +++ b/test/langtools/tools/javac/patterns/BreakAndLoops.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -237,4 +237,4 @@ public class BreakAndLoops extends ComboInstance { return code; } } -} \ No newline at end of file +} diff --git a/test/langtools/tools/javac/preview/PreviewAutoSuppress.java b/test/langtools/tools/javac/preview/PreviewAutoSuppress.java index 058ccdf0a2a..f2096aff648 100644 --- a/test/langtools/tools/javac/preview/PreviewAutoSuppress.java +++ b/test/langtools/tools/javac/preview/PreviewAutoSuppress.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -44,6 +44,8 @@ import java.nio.file.Paths; import java.util.List; public class PreviewAutoSuppress extends TestRunner { + // Major version number (e.g. '27'). + private static final String FEATURE_VERSION = String.valueOf(Runtime.version().feature()); protected ToolBox tb; @@ -83,7 +85,7 @@ public class PreviewAutoSuppress extends TestRunner { List log = new JavacTask(tb, Task.Mode.CMDLINE) .outdir(classes) .options("--enable-preview", - "-source", String.valueOf(Runtime.version().feature()), + "-source", FEATURE_VERSION, "-Xlint:preview", "-XDforcePreview", "-XDrawDiagnostics") @@ -182,7 +184,7 @@ public class PreviewAutoSuppress extends TestRunner { "--add-exports", "java.base/preview.api=ALL-UNNAMED", "--enable-preview", "-Xlint:preview", - "-source", String.valueOf(Runtime.version().feature()), + "-source", FEATURE_VERSION, "-XDrawDiagnostics") .files(tb.findJavaFiles(testSrc)) .run() diff --git a/test/langtools/tools/javac/records/RecordReading.java b/test/langtools/tools/javac/records/RecordReading.java index 6133a04fca9..e57a7f77ceb 100644 --- a/test/langtools/tools/javac/records/RecordReading.java +++ b/test/langtools/tools/javac/records/RecordReading.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -31,7 +31,6 @@ * @run main RecordReading */ - import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; diff --git a/test/langtools/tools/javac/recovery/ClassBlockExits.java b/test/langtools/tools/javac/recovery/ClassBlockExits.java index b1c54b456ae..3ac04cbe991 100644 --- a/test/langtools/tools/javac/recovery/ClassBlockExits.java +++ b/test/langtools/tools/javac/recovery/ClassBlockExits.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -138,4 +138,4 @@ public class ClassBlockExits extends ComboInstance { return code; } } -} \ No newline at end of file +} diff --git a/test/langtools/tools/javac/types/IsFunctionalInterfaceTest.java b/test/langtools/tools/javac/types/IsFunctionalInterfaceTest.java new file mode 100644 index 00000000000..a4fe4e470c7 --- /dev/null +++ b/test/langtools/tools/javac/types/IsFunctionalInterfaceTest.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2026, 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 8378906 + * @summary Check that Types.isFunctionalInterface() works for interface methods + * with ACC_PUBLIC, ACC_BRIDGE, ACC_ABSTRACT, ACC_SYNTHETIC flags. + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit IsFunctionalInterfaceTest + */ + +import java.lang.classfile.ClassFile; +import java.lang.classfile.MethodElement; +import java.lang.classfile.MethodModel; +import java.lang.reflect.AccessFlag; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; + +import toolbox.JavacTask; +import toolbox.ToolBox; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import toolbox.Task; + +public class IsFunctionalInterfaceTest { + + Path base; + ToolBox tb = new ToolBox(); + + @Test + void test8378906() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString()) + .sources(""" + interface I { + @Deprecated + Object test(); + } + + final class Sub implements I { + public static final Sub INSTANCE = new Sub(); + public Object test() { return null; } + } + + class Util { + public static boolean foo(I a) { return true; } + public static boolean foo(Sub a) { return false; } + } + """) + .run() + .writeAll(); + + Path path = classes.resolve("I.class"); + ClassFile classFile = ClassFile.of(); + byte[] bytes = classFile.transformClass(classFile.parse(path), + (classBuilder, classElement) -> { + if (classElement instanceof MethodModel mm + && mm.methodName().equalsString("test")) { + int flags = mm.flags().flagsMask() | AccessFlag.BRIDGE.mask() | AccessFlag.SYNTHETIC.mask(); + classBuilder.withMethod(mm.methodName(), mm.methodType(), flags, (methodBuilder) -> { + mm.attributes().forEach(attr -> { + if (attr instanceof MethodElement me) { + methodBuilder.with(me); + } + }); + }); + } else { + classBuilder.with(classElement); + } + }); + Files.write(path, bytes); + + new JavacTask(tb) + .options("-d", classes.toString(), "-cp", classes.toString()) + .sources(""" + public class Test { + public void main() { Util.foo(Sub.INSTANCE); } + } + """) + .run() + .writeAll(); + + List out1 = new JavacTask(tb) + .options("-d", classes.toString(), "-cp", classes.toString(), "-XDrawDiagnostics", "-nowarn") + .sources(""" + public class Test { + public void main() { t(() -> null); } + private void t(I i) {} + } + """) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out1, List.of( + "Test.java:2:25: compiler.err.cant.apply.symbol: kindname.method, t, I, @27, kindname.class, Test, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.not.a.functional.intf.1: I, (compiler.misc.no.abstracts: kindname.interface, I)))", + "1 error")); + + List out2 = new JavacTask(tb) + .options("-d", classes.toString(), "-cp", classes.toString(), "-XDrawDiagnostics", "-nowarn") + .sources(""" + public class Impl implements I { + } + """) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out2, List.of( + "Impl.java:1:8: compiler.err.does.not.override.abstract: Impl, test(), I", + "1 error")); + + new JavacTask(tb) + .options("-d", classes.toString(), "-cp", classes.toString()) + .sources(""" + public class Impl implements I { + public Object test() { return null; } + } + """) + .run() + .writeAll(); + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } +} diff --git a/test/langtools/tools/javap/T4975569.java b/test/langtools/tools/javap/T4975569.java index c43598b41db..ce75ba84586 100644 --- a/test/langtools/tools/javap/T4975569.java +++ b/test/langtools/tools/javap/T4975569.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, 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 @@ -105,4 +105,3 @@ public class T4975569 { protected class Prot { } private class Priv { int i; } } - diff --git a/test/lib-test/jdk/test/lib/TestMutuallyExclusivePlatformPredicates.java b/test/lib-test/jdk/test/lib/TestMutuallyExclusivePlatformPredicates.java index 8047b81c1e6..801874f38c2 100644 --- a/test/lib-test/jdk/test/lib/TestMutuallyExclusivePlatformPredicates.java +++ b/test/lib-test/jdk/test/lib/TestMutuallyExclusivePlatformPredicates.java @@ -50,7 +50,7 @@ public class TestMutuallyExclusivePlatformPredicates { OS("isAix", "isLinux", "isOSX", "isWindows"), VM_TYPE("isClient", "isServer", "isMinimal", "isZero", "isEmbedded"), MODE("isInt", "isMixed", "isComp"), - IGNORED("isEmulatedClient", "isDebugBuild", "isFastDebugBuild", "isMusl", + IGNORED("isDebugBuild", "isFastDebugBuild", "isMusl", "isStatic", "isSlowDebugBuild", "hasSA", "isRoot", "isTieredSupported", "areCustomLoadersSupportedForCDS", "isDefaultCDSArchiveSupported", "isHardenedOSX", "hasOSXPlistEntries", "isOracleLinux7", "isOnWayland"); diff --git a/test/lib/jdk/test/lib/Convert.java b/test/lib/jdk/test/lib/Convert.java index 7ed1112c94d..2738e04c2f0 100644 --- a/test/lib/jdk/test/lib/Convert.java +++ b/test/lib/jdk/test/lib/Convert.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -41,26 +41,6 @@ public class Convert { return result; } - /* - * Convert a hexadecimal string to the corresponding little-ending number - * as a BigInteger. The clearHighBit argument determines whether the most - * significant bit of the highest byte should be set to 0 in the result. - */ - public static - BigInteger hexStringToBigInteger(boolean clearHighBit, String str) { - BigInteger result = BigInteger.ZERO; - for (int i = 0; i < str.length() / 2; i++) { - int curVal = Character.digit(str.charAt(2 * i), 16); - curVal <<= 4; - curVal += Character.digit(str.charAt(2 * i + 1), 16); - if (clearHighBit && i == str.length() / 2 - 1) { - curVal &= 0x7F; - } - result = result.add(BigInteger.valueOf(curVal).shiftLeft(8 * i)); - } - return result; - } - private static EdECPoint byteArrayToEdPoint(byte[] arr) { byte msb = arr[arr.length - 1]; boolean xOdd = (msb & 0x80) != 0; diff --git a/test/lib/jdk/test/lib/Platform.java b/test/lib/jdk/test/lib/Platform.java index 170e53930d8..892de2338e3 100644 --- a/test/lib/jdk/test/lib/Platform.java +++ b/test/lib/jdk/test/lib/Platform.java @@ -75,10 +75,6 @@ public class Platform { return vmInfo.contains("static"); } - public static boolean isEmulatedClient() { - return vmInfo.contains(" emulated-client"); - } - public static boolean isTieredSupported() { return (compiler != null) && compiler.contains("Tiered Compilers"); } diff --git a/test/lib/jdk/test/lib/cds/CDSTestUtils.java b/test/lib/jdk/test/lib/cds/CDSTestUtils.java index 13ac2e4e97a..1c51a693907 100644 --- a/test/lib/jdk/test/lib/cds/CDSTestUtils.java +++ b/test/lib/jdk/test/lib/cds/CDSTestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -696,11 +696,16 @@ public class CDSTestUtils { System.out.println("[STDOUT]\n" + output.getStdout()); if (output.getExitValue() != 0 && output.getStdout().contains("A fatal error has been detected")) { - throw new RuntimeException("Hotspot crashed"); + throw new RuntimeException(getCrashMessage(output.getStdout())); } return output; } + static String getCrashMessage(String stdOut) { + int start = stdOut.indexOf("# A fatal error has been detected by the Java Runtime Environment:"); + int end = stdOut.indexOf(".log", start) + 4; + return stdOut.substring(start, end); + } private static void writeFile(File file, String content) throws Exception { FileOutputStream fos = new FileOutputStream(file); diff --git a/test/lib/jdk/test/lib/cli/CommandLineOptionTest.java b/test/lib/jdk/test/lib/cli/CommandLineOptionTest.java index a9ae57bca71..48738d4a0da 100644 --- a/test/lib/jdk/test/lib/cli/CommandLineOptionTest.java +++ b/test/lib/jdk/test/lib/cli/CommandLineOptionTest.java @@ -201,10 +201,6 @@ public abstract class CommandLineOptionTest { if (!Platform.isStatic()) { finalOptions.add(CommandLineOptionTest.getVMTypeOption()); } - String extraFlagForEmulated = CommandLineOptionTest.getVMTypeOptionForEmulated(); - if (extraFlagForEmulated != null) { - finalOptions.add(extraFlagForEmulated); - } Collections.addAll(finalOptions, options); CommandLineOptionTest.verifyJVMStartup(expectedMessages, @@ -401,10 +397,6 @@ public abstract class CommandLineOptionTest { if (!Platform.isStatic()) { finalOptions.add(CommandLineOptionTest.getVMTypeOption()); } - String extraFlagForEmulated = CommandLineOptionTest.getVMTypeOptionForEmulated(); - if (extraFlagForEmulated != null) { - finalOptions.add(extraFlagForEmulated); - } Collections.addAll(finalOptions, additionalVMOpts); CommandLineOptionTest.verifyOptionValue(optionName, expectedValue, @@ -512,18 +504,6 @@ public abstract class CommandLineOptionTest { throw new RuntimeException("Unknown VM mode."); } - /** - * @return addtional VMoptions(Emulated related) required to start a new VM with the same type as current. - */ - private static String getVMTypeOptionForEmulated() { - if (Platform.isServer() && !Platform.isEmulatedClient()) { - return "-XX:-NeverActAsServerClassMachine"; - } else if (Platform.isEmulatedClient()) { - return "-XX:+NeverActAsServerClassMachine"; - } - return null; - } - private final BooleanSupplier predicate; /** diff --git a/test/lib/jdk/test/lib/process/OutputAnalyzer.java b/test/lib/jdk/test/lib/process/OutputAnalyzer.java index d8b3f470260..553e13b28ff 100644 --- a/test/lib/jdk/test/lib/process/OutputAnalyzer.java +++ b/test/lib/jdk/test/lib/process/OutputAnalyzer.java @@ -56,8 +56,23 @@ public final class OutputAnalyzer { * @throws IOException If an I/O error occurs. */ public OutputAnalyzer(Process process, Charset cs) throws IOException { - buffer = OutputBuffer.of(process, cs); + this(process, cs, true); } + + /** + * Create an OutputAnalyzer, a utility class for verifying output and exit + * value from a Process, with a configurable verbosity level. + * + * @param process Process to analyze + * @param cs The charset used to convert stdout/stderr from bytes to chars + * or null for the default charset. + * @param verbose Set to false to limit logging to stdout. + * @throws IOException If an I/O error occurs. + */ + public OutputAnalyzer(Process process, Charset cs, boolean verbose) throws IOException { + buffer = OutputBuffer.of(process, cs, verbose); + } + /** * Create an OutputAnalyzer, a utility class for verifying output and exit * value from a Process @@ -66,7 +81,19 @@ public final class OutputAnalyzer { * @throws IOException If an I/O error occurs. */ public OutputAnalyzer(Process process) throws IOException { - buffer = OutputBuffer.of(process); + this(process, true); + } + + /** + * Create an OutputAnalyzer, a utility class for verifying output and exit + * value from a Process, with a configurable verbosity level. + * + * @param process Process to analyze + * @param verbose Set to false to limit logging to stdout. + * @throws IOException If an I/O error occurs. + */ + public OutputAnalyzer(Process process, boolean verbose) throws IOException { + buffer = OutputBuffer.of(process, verbose); } /** diff --git a/test/lib/jdk/test/lib/process/OutputBuffer.java b/test/lib/jdk/test/lib/process/OutputBuffer.java index 0390535bf89..57e00aa73c7 100644 --- a/test/lib/jdk/test/lib/process/OutputBuffer.java +++ b/test/lib/jdk/test/lib/process/OutputBuffer.java @@ -89,12 +89,20 @@ public interface OutputBuffer { */ public long pid(); + public static OutputBuffer of(Process p, boolean quiet) { + return of(p, null, quiet); + } + public static OutputBuffer of(Process p, Charset cs) { - return new LazyOutputBuffer(p, cs); + return of(p, cs, false); } public static OutputBuffer of(Process p) { - return new LazyOutputBuffer(p, null); + return of(p, null, false); + } + + public static OutputBuffer of(Process p, Charset cs, boolean quiet) { + return new LazyOutputBuffer(p, cs, quiet); } public static OutputBuffer of(String stdout, String stderr, int exitValue) { @@ -130,19 +138,23 @@ public interface OutputBuffer { } } + private final boolean verbose; private final StreamTask outTask; private final StreamTask errTask; private final Process p; private volatile Integer exitValue; // null implies we don't yet know private final void logProgress(String state) { + if (verbose) { System.out.println("[" + Instant.now().toString() + "] " + state - + " for process " + p.pid()); + + " for process " + p.pid()); System.out.flush(); + } } - private LazyOutputBuffer(Process p, Charset cs) { + private LazyOutputBuffer(Process p, Charset cs, boolean verbose) { this.p = p; + this.verbose = verbose; logProgress("Gathering output"); outTask = new StreamTask(p.getInputStream(), cs); errTask = new StreamTask(p.getErrorStream(), cs); diff --git a/test/lib/native/testlib_thread_barriers.h b/test/lib/native/testlib_thread_barriers.h new file mode 100644 index 00000000000..8e0b717b57a --- /dev/null +++ b/test/lib/native/testlib_thread_barriers.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corp. + * 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. + */ + +#ifndef TESTLIB_THREAD_BARRIERS_H +#define TESTLIB_THREAD_BARRIERS_H + +/* MacOS does not have pthread barriers; implement a fallback using condvars. */ + +#ifndef _WIN32 +#if !defined _POSIX_BARRIERS || _POSIX_BARRIERS < 0 + +#include + +#define PTHREAD_BARRIER_SERIAL_THREAD 1 + +#define pthread_barrier_t barr_t +#define pthread_barrier_init(barr, attr, need) barr_init(barr, attr, need) +#define pthread_barrier_destroy(barr) barr_destroy(barr) +#define pthread_barrier_wait(barr) barr_wait(barr) + +typedef struct { + pthread_mutex_t mutex; + pthread_cond_t cond; + int have, need, trigger_count; +} barr_t; + +int barr_init(barr_t* b, void* ignored, int need) { + b->have = b->trigger_count = 0; + b->need = need; + pthread_mutex_init(&b->mutex, NULL); + pthread_cond_init(&b->cond, NULL); + return 0; +} + +int barr_destroy(barr_t* b) { + pthread_mutex_destroy(&b->mutex); + pthread_cond_destroy(&b->cond); + return 0; +} + +int barr_wait(barr_t* b) { + pthread_mutex_lock(&b->mutex); + int my_trigger_count = b->trigger_count; + b->have++; + if (b->have == b->need) { + b->have = 0; + b->trigger_count++; + pthread_cond_broadcast(&b->cond); + pthread_mutex_unlock(&b->mutex); + return PTHREAD_BARRIER_SERIAL_THREAD; + } + while (my_trigger_count == b->trigger_count) { // no spurious wakeups + pthread_cond_wait(&b->cond, &b->mutex); + } + pthread_mutex_unlock(&b->mutex); + return 0; +} + +#endif // !_POSIX_BARRIERS +#endif // !_WIN32 + +#endif // TESTLIB_THREAD_BARRIERS_H diff --git a/test/micro/org/openjdk/bench/java/net/URLToString.java b/test/micro/org/openjdk/bench/java/net/URLToString.java new file mode 100644 index 00000000000..38f6500c573 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/net/URLToString.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2026, 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 org.openjdk.bench.java.net; + +import org.openjdk.jmh.annotations.*; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.concurrent.TimeUnit; + +/** + * Tests java.net.URL.toString performance + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(value = 3) +public class URLToString { + + @Param({"false", "true"}) + boolean auth; + + @Param({"false", "true"}) + boolean query; + + @Param({"false", "true"}) + boolean ref; + + private URL url; + + @Setup() + public void setup() throws MalformedURLException { + StringBuilder sb = new StringBuilder(); + if (auth) { + sb.append("http://hostname"); + } else { + sb.append("file:"); + } + sb.append("/some/long/path/to/jar/app-1.0.jar!/org/summerframework/samples/horseclinic/HorseClinicApplication.class"); + if (query) { + sb.append("?param=value"); + } + if (ref) { + sb.append("#fragment"); + } + + url = URI.create(sb.toString()).toURL(); + } + + @Benchmark + public String urlToString() { + return url.toString(); + } +}